diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5f706445..958de340 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,114 +12,89 @@ on: jobs: setup: + name: Setup runs-on: ubuntu-latest + outputs: + cache-hit: ${{ steps.setup_uv.outputs.cache-hit }} steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 + - name: Install uv (+ enable cache) + id: setup_uv + uses: astral-sh/setup-uv@v6 with: - python-version: 3.12 + enable-cache: true - - name: Cache pip - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- + - name: Sync dependencies + run: uv sync --locked --dev - - name: Cache virtual environment - uses: actions/cache@v4 - with: - path: .venv - key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - restore-keys: | - ${{ runner.os }}-venv- - - - name: Install dependencies - run: | - python -m venv .venv - source .venv/bin/activate - pip install --upgrade pip - pip install poetry - poetry install + - name: Prune uv cache (optimized for CI) + run: uv cache prune --ci ruff-format: + name: ruff-format needs: setup runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Restore virtual environment cache - uses: actions/cache@v4 + - name: Install uv + restore cache + uses: astral-sh/setup-uv@v6 with: - path: .venv - key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - restore-keys: | - ${{ runner.os }}-venv- - - - name: Check ruff version - run: | - source .venv/bin/activate - poetry run ruff --version + enable-cache: true - - name: Run ruff format check - run: | - source .venv/bin/activate - poetry run ruff format --check . + - name: Check ruff formatting + run: uv run ruff format --check . ruff-lint: + name: ruff-lint needs: setup runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Restore virtual environment cache - uses: actions/cache@v4 + - name: Install uv + restore cache + uses: astral-sh/setup-uv@v6 with: - path: .venv - key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - restore-keys: | - ${{ runner.os }}-venv- - - - name: Set up Python environment - run: source .venv/bin/activate - - - name: Check ruff version - run: | - source .venv/bin/activate - poetry run ruff --version + enable-cache: true - name: Run ruff lint - run: | - source .venv/bin/activate - poetry run ruff check --output-format=github . + run: uv run ruff check --output-format=github . mypy-type-check: + name: mypy-type-check needs: setup runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Restore virtual environment cache - uses: actions/cache@v4 + - name: Install uv + restore cache + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Run mypy + run: uv run --frozen mypy . + + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: - path: .venv - key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - restore-keys: | - ${{ runner.os }}-venv- - - - name: Check mypy version - run: | - source .venv/bin/activate - poetry run mypy --version - - - name: Run mypy type check - run: | - source .venv/bin/activate - poetry run mypy . + java-version: "17" + distribution: "temurin" + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Publish package + run: cd java && ./gradlew publish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/document.yaml b/.github/workflows/document.yaml new file mode 100644 index 00000000..a70bca90 --- /dev/null +++ b/.github/workflows/document.yaml @@ -0,0 +1,70 @@ +name: document + +on: + workflow_dispatch: + push: + branches: + - main + - develop + paths: + - "docs/**" + pull_request: + branches: + - main + - develop + paths: + - "docs/**" + +concurrency: + group: "pages" + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Cache virtual environment + id: cached-virtualenv + uses: actions/cache@v4 + with: + path: .venv + key: ${{ runner.os }}-venv-${{ hashFiles('**/uv.lock') }} + + - name: Install dependencies + if: steps.cached-virtualenv.outputs.cache-hit != 'true' + run: | + python -m venv .venv + source .venv/bin/activate + pip install --upgrade pip + pip install uv + uv sync + + - name: Build documentation + run: | + source .venv/bin/activate + cd docs + python build.py + + - name: Redirect to the en documentation + run: | + username=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 1) + repository=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 2) + echo '' > ./docs/pages/index.html + + - name: Deploy documentation + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/pages diff --git a/.gitignore b/.gitignore index 5268a8be..ab0dc67d 100644 --- a/.gitignore +++ b/.gitignore @@ -159,7 +159,17 @@ cython_debug/ # 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/ +.idea/ # ruff .ruff_cache/ + +# vscode +.vscode/ + +# MacOS +.DS_Store + +# ADF +agent.log* +!java/lib/src/main/java/adf_core_python/core/agent/precompute diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..24ee5b1b --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/README.md b/README.md index d0692ebb..d0f490a8 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,41 @@ # adf-core-python +## Documentation + +[ADF Core Python Documentation](https://adf-python.github.io/adf-core-python/) + ## Development Environment ### Prerequisites -- Python (3.12 or higher) -- Poetry (1.8.3 or higher) +- Python (3.13 or higher) +- uv (0.8.2 or higher) ### Installation ```bash -poetry install +uv sync +``` + +### Run Agent + +```bash +uv run python ./adf_core_python/launcher.py + +# get help +uv run python ./adf_core_python/launcher.py -h ``` ### Build ```bash -poetry build +uv build ``` ### Pre Commit ```bash -poetry run task precommit +uv run ruff format . +uv run ruff check . +uv run mypy . ``` diff --git a/adf_core_python/main.py b/adf_core_python/main.py deleted file mode 100644 index 7df869a1..00000000 --- a/adf_core_python/main.py +++ /dev/null @@ -1 +0,0 @@ -print("Hello, World!") diff --git a/config/development.json b/config/development.json new file mode 100644 index 00000000..d0ae716d --- /dev/null +++ b/config/development.json @@ -0,0 +1,3 @@ +{ + "test": "test" +} diff --git a/config/launcher.yaml b/config/launcher.yaml new file mode 100644 index 00000000..7423a6c3 --- /dev/null +++ b/config/launcher.yaml @@ -0,0 +1,44 @@ +kernel: + host: localhost + port: 27931 + +gateway: + host: localhost + port: 27941 + +team: + name: AIT-Rescue + +adf: + launcher: + precompute: false + debug: + flag: false + agent: + moduleconfig: + filename: config/module.yaml + precompute: + dir_name: precompute + + develop: + flag: true + filename: config/development.json + + team: + platoon: + ambulance: + count: 100 + fire: + count: 100 + police: + count: 100 + office: + ambulance: + count: 5 + fire: + count: 5 + police: + count: 5 + + gateway: + flag: false diff --git a/config/module.yaml b/config/module.yaml new file mode 100644 index 00000000..7d00fd80 --- /dev/null +++ b/config/module.yaml @@ -0,0 +1,85 @@ +DefaultTacticsAmbulanceTeam: + HumanDetector: adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector + Search: adf_core_python.implement.module.complex.default_search.DefaultSearch + ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorAmbulance: adf_core_python.implement.centralized.default_command_executor_ambulance.DefaultCommandExecutorAmbulance + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsFireBrigade: + HumanDetector: adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector + Search: adf_core_python.implement.module.complex.default_search.DefaultSearch + ExtendActionRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorFire: adf_core_python.implement.centralized.default_command_executor_fire.DefaultCommandExecutorFire + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsPoliceForce: + RoadDetector: adf_core_python.implement.module.complex.default_road_detector.DefaultRoadDetector + Search: adf_core_python.implement.module.complex.default_search.DefaultSearch + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorPolice: adf_core_python.implement.centralized.default_command_executor_police.DefaultCommandExecutorPolice + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsAmbulanceCenter: + TargetAllocator: adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_ambulance.DefaultCommandPickerAmbulance + +DefaultTacticsFireStation: + TargetAllocator: adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_fire.DefaultCommandPickerFire + +DefaultTacticsPoliceOffice: + TargetAllocator: adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_police.DefaultCommandPickerPolice + +DefaultSearch: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + Clustering: adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering + +DefaultRoadDetector: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultHumanDetector: + Clustering: adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering + +DefaultExtendActionClear: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionRescue: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionMove: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionTransport: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultCommandExecutorAmbulance: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorFire: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionFireRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorScout: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultCommandExecutorScoutPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + +MessageManager: + PlatoonChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + CenterChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + PlatoonMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator + CenterMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..be436389 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +source/adf_core_python.* +pages diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..4d78f237 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +# sphinx-documentation + +## Generate documentation + +```bash +sphinx-apidoc -f -o ./docs/source/adf_core_python ./adf_core_python +sphinx-build -M html ./docs/source/adf_core_python ./docs/build -a +``` diff --git a/docs/build.py b/docs/build.py new file mode 100644 index 00000000..920d1d21 --- /dev/null +++ b/docs/build.py @@ -0,0 +1,43 @@ +import os +import subprocess +import yaml + +# a single build step, which keeps conf.py and versions.yaml at the main branch +# in generall we use environment variables to pass values to conf.py, see below +# and runs the build as we did locally +def build_doc(version, language, tag): + os.environ["current_version"] = version + os.environ["current_language"] = language + subprocess.run("git checkout " + tag, shell=True) + subprocess.run("git checkout main -- conf.py", shell=True) + subprocess.run("git checkout main -- versions.yaml", shell=True) + subprocess.run("doxygen Doxyfile", shell=True) + os.environ['SPHINXOPTS'] = "-D language='{}'".format(language) + subprocess.run("make html", shell=True) + +# a move dir method because we run multiple builds and bring the html folders to a +# location which we then push to github pages +def move_dir(src, dst): + subprocess.run(["mkdir", "-p", dst]) + subprocess.run("mv "+src+'* ' + dst, shell=True) + +# to separate a single local build from all builds we have a flag, see conf.py +os.environ["build_all_docs"] = str(True) +os.environ["pages_root"] = "https://adf-python.github.io/adf-core-python/" + +# manually the main branch build in the current supported languages +build_doc("latest", "en", "main") +move_dir("./build/html/", "./pages/en") +build_doc("latest", "ja", "main") +move_dir("./build/html/", "./pages/ja/") + +# reading the yaml file +with open("versions.yaml", "r") as yaml_file: + docs = yaml.safe_load(yaml_file) + +# and looping over all values to call our build with version, language and its tag +# for version, details in docs.items(): +# tag = details.get('tag', '') +# for language in details.get('languages', []): +# build_doc(version, language, version) +# move_dir("./build/html/", "./pages/"+version+'/'+language+'/') diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..747ffb7b --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/_static/.gitkeep b/docs/source/_static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/_templates/versions.html b/docs/source/_templates/versions.html new file mode 100644 index 00000000..0b3a1c81 --- /dev/null +++ b/docs/source/_templates/versions.html @@ -0,0 +1,96 @@ + + +
+ + Language: {{ current_language }} + + +
+ {% if languages|length >= 1 %} +
+
{{ _('Languages') }}
+
+ {% for the_language, url in languages %} + {{ the_language }} + {% endfor %} +
+
+ {% endif %} + +
+
+ + diff --git a/docs/source/adf_core_python/adf_core_python.cli.rst b/docs/source/adf_core_python/adf_core_python.cli.rst new file mode 100644 index 00000000..202435d6 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.cli.rst @@ -0,0 +1,21 @@ +adf\_core\_python.cli package +============================= + +Submodules +---------- + +adf\_core\_python.cli.cli module +-------------------------------- + +.. automodule:: adf_core_python.cli.cli + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.cli + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.action.ambulance.rst b/docs/source/adf_core_python/adf_core_python.core.agent.action.ambulance.rst new file mode 100644 index 00000000..6850cac0 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.action.ambulance.rst @@ -0,0 +1,37 @@ +adf\_core\_python.core.agent.action.ambulance package +===================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.action.ambulance.action\_load module +----------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.ambulance.action_load + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.ambulance.action\_rescue module +------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.ambulance.action_rescue + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.ambulance.action\_unload module +------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.ambulance.action_unload + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.action.ambulance + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.action.common.rst b/docs/source/adf_core_python/adf_core_python.core.agent.action.common.rst new file mode 100644 index 00000000..28fdba8d --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.action.common.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.agent.action.common package +================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.action.common.action\_move module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.common.action_move + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.common.action\_rest module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.common.action_rest + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.action.common + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.action.fire.rst b/docs/source/adf_core_python/adf_core_python.core.agent.action.fire.rst new file mode 100644 index 00000000..c3fe550b --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.action.fire.rst @@ -0,0 +1,37 @@ +adf\_core\_python.core.agent.action.fire package +================================================ + +Submodules +---------- + +adf\_core\_python.core.agent.action.fire.action\_extinguish module +------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.agent.action.fire.action_extinguish + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.fire.action\_refill module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.fire.action_refill + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.fire.action\_rescue module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.fire.action_rescue + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.action.fire + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.action.police.rst b/docs/source/adf_core_python/adf_core_python.core.agent.action.police.rst new file mode 100644 index 00000000..c3d899c1 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.action.police.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.agent.action.police package +================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.action.police.action\_clear module +--------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.police.action_clear + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.action.police.action\_clear\_area module +--------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.police.action_clear_area + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.action.police + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.action.rst b/docs/source/adf_core_python/adf_core_python.core.agent.action.rst new file mode 100644 index 00000000..fbb4898c --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.action.rst @@ -0,0 +1,32 @@ +adf\_core\_python.core.agent.action package +=========================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent.action.ambulance + adf_core_python.core.agent.action.common + adf_core_python.core.agent.action.fire + adf_core_python.core.agent.action.police + +Submodules +---------- + +adf\_core\_python.core.agent.action.action module +------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.action.action + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.action + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.rst new file mode 100644 index 00000000..8b8db00b --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.agent.communication package +================================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent.communication.standard + +Submodules +---------- + +adf\_core\_python.core.agent.communication.message\_manager module +------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.agent.communication.message_manager + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.centralized.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.centralized.rst new file mode 100644 index 00000000..45fa534e --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.centralized.rst @@ -0,0 +1,53 @@ +adf\_core\_python.core.agent.communication.standard.bundle.centralized package +============================================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.communication.standard.bundle.centralized.command\_ambulance module +------------------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.centralized.command\_fire module +------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.centralized.command\_police module +--------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized.command_police + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.centralized.command\_scout module +-------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.centralized.message\_report module +--------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized.message_report + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.centralized + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.information.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.information.rst new file mode 100644 index 00000000..1866f085 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.information.rst @@ -0,0 +1,61 @@ +adf\_core\_python.core.agent.communication.standard.bundle.information package +============================================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_ambulance\_team module +------------------------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_building module +----------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_building + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_civilian module +----------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_civilian + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_fire\_brigade module +---------------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_police\_force module +---------------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_police_force + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.information.message\_road module +------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information.message_road + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.information + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.rst new file mode 100644 index 00000000..e5a03c50 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.bundle.rst @@ -0,0 +1,38 @@ +adf\_core\_python.core.agent.communication.standard.bundle package +================================================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent.communication.standard.bundle.centralized + adf_core_python.core.agent.communication.standard.bundle.information + +Submodules +---------- + +adf\_core\_python.core.agent.communication.standard.bundle.standard\_message module +----------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.standard_message + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.bundle.standard\_message\_priority module +--------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle.standard_message_priority + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.bundle + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.rst new file mode 100644 index 00000000..d3fd611e --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.rst @@ -0,0 +1,30 @@ +adf\_core\_python.core.agent.communication.standard package +=========================================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent.communication.standard.bundle + adf_core_python.core.agent.communication.standard.utility + +Submodules +---------- + +adf\_core\_python.core.agent.communication.standard.standard\_communication\_module module +------------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.agent.communication.standard.standard_communication_module + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication.standard + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.utility.rst b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.utility.rst new file mode 100644 index 00000000..afb37b3f --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.communication.standard.utility.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.agent.communication.standard.utility package +=================================================================== + +Submodules +---------- + +adf\_core\_python.core.agent.communication.standard.utility.apply\_to\_world\_info module +----------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.utility.apply_to_world_info + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.communication.standard.utility.bitarray\_with\_exits\_flag module +---------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.communication.standard.utility + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.config.rst b/docs/source/adf_core_python/adf_core_python.core.agent.config.rst new file mode 100644 index 00000000..baf652d5 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.config.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.agent.config package +=========================================== + +Submodules +---------- + +adf\_core\_python.core.agent.config.module\_config module +--------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.config.module_config + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.config + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.develop.rst b/docs/source/adf_core_python/adf_core_python.core.agent.develop.rst new file mode 100644 index 00000000..3ab6ca9f --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.develop.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.agent.develop package +============================================ + +Submodules +---------- + +adf\_core\_python.core.agent.develop.develop\_data module +--------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.develop.develop_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.develop + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.info.rst b/docs/source/adf_core_python/adf_core_python.core.agent.info.rst new file mode 100644 index 00000000..9bf3a658 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.info.rst @@ -0,0 +1,37 @@ +adf\_core\_python.core.agent.info package +========================================= + +Submodules +---------- + +adf\_core\_python.core.agent.info.agent\_info module +---------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.info.agent_info + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.info.scenario\_info module +------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.info.scenario_info + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.info.world\_info module +---------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.info.world_info + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.info + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.module.rst b/docs/source/adf_core_python/adf_core_python.core.agent.module.rst new file mode 100644 index 00000000..a9711493 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.module.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.agent.module package +=========================================== + +Submodules +---------- + +adf\_core\_python.core.agent.module.module\_manager module +---------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.module.module_manager + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.module + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.platoon.rst b/docs/source/adf_core_python/adf_core_python.core.agent.platoon.rst new file mode 100644 index 00000000..ce60c8f6 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.platoon.rst @@ -0,0 +1,45 @@ +adf\_core\_python.core.agent.platoon package +============================================ + +Submodules +---------- + +adf\_core\_python.core.agent.platoon.platoon module +--------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.platoon.platoon + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.platoon.platoon\_ambulance module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.platoon.platoon_ambulance + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.platoon.platoon\_fire module +--------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.platoon.platoon_fire + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.agent.platoon.platoon\_police module +----------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.platoon.platoon_police + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.platoon + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.precompute.rst b/docs/source/adf_core_python/adf_core_python.core.agent.precompute.rst new file mode 100644 index 00000000..1e1c752d --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.precompute.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.agent.precompute package +=============================================== + +Submodules +---------- + +adf\_core\_python.core.agent.precompute.precompute\_data module +--------------------------------------------------------------- + +.. automodule:: adf_core_python.core.agent.precompute.precompute_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent.precompute + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.agent.rst b/docs/source/adf_core_python/adf_core_python.core.agent.rst new file mode 100644 index 00000000..c62ed6d8 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.agent.rst @@ -0,0 +1,36 @@ +adf\_core\_python.core.agent package +==================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent.action + adf_core_python.core.agent.communication + adf_core_python.core.agent.config + adf_core_python.core.agent.develop + adf_core_python.core.agent.info + adf_core_python.core.agent.module + adf_core_python.core.agent.platoon + adf_core_python.core.agent.precompute + +Submodules +---------- + +adf\_core\_python.core.agent.agent module +----------------------------------------- + +.. automodule:: adf_core_python.core.agent.agent + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.agent + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.action.rst b/docs/source/adf_core_python/adf_core_python.core.component.action.rst new file mode 100644 index 00000000..41a180b4 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.action.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.component.action package +=============================================== + +Submodules +---------- + +adf\_core\_python.core.component.action.extend\_action module +------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.action.extend_action + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.action + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.communication.rst b/docs/source/adf_core_python/adf_core_python.core.component.communication.rst new file mode 100644 index 00000000..92879f4a --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.communication.rst @@ -0,0 +1,45 @@ +adf\_core\_python.core.component.communication package +====================================================== + +Submodules +---------- + +adf\_core\_python.core.component.communication.channel\_subscriber module +------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.communication.channel_subscriber + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.communication.communication\_message module +---------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.communication.communication_message + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.communication.communication\_module module +--------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.communication.communication_module + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.communication.message\_coordinator module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.communication.message_coordinator + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.communication + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.module.algorithm.rst b/docs/source/adf_core_python/adf_core_python.core.component.module.algorithm.rst new file mode 100644 index 00000000..6ad25d3f --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.module.algorithm.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.component.module.algorithm package +========================================================= + +Submodules +---------- + +adf\_core\_python.core.component.module.algorithm.clustering module +------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.algorithm.clustering + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.algorithm.path\_planning module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.algorithm.path_planning + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.module.algorithm + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.module.complex.rst b/docs/source/adf_core_python/adf_core_python.core.component.module.complex.rst new file mode 100644 index 00000000..86de2b82 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.module.complex.rst @@ -0,0 +1,77 @@ +adf\_core\_python.core.component.module.complex package +======================================================= + +Submodules +---------- + +adf\_core\_python.core.component.module.complex.ambulance\_target\_allocator module +----------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.ambulance_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.fire\_target\_allocator module +------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.component.module.complex.fire_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.human\_detector module +---------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.human_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.police\_target\_allocator module +-------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.police_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.road\_detector module +--------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.road_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.search module +------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.search + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.target\_allocator module +------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.component.module.complex.target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.module.complex.target\_detector module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.complex.target_detector + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.module.complex + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.module.rst b/docs/source/adf_core_python/adf_core_python.core.component.module.rst new file mode 100644 index 00000000..f07feacf --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.module.rst @@ -0,0 +1,30 @@ +adf\_core\_python.core.component.module package +=============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.component.module.algorithm + adf_core_python.core.component.module.complex + +Submodules +---------- + +adf\_core\_python.core.component.module.abstract\_module module +--------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.module.abstract_module + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.module + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.rst b/docs/source/adf_core_python/adf_core_python.core.component.rst new file mode 100644 index 00000000..c33224e9 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.rst @@ -0,0 +1,32 @@ +adf\_core\_python.core.component package +======================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.component.action + adf_core_python.core.component.communication + adf_core_python.core.component.module + adf_core_python.core.component.tactics + +Submodules +---------- + +adf\_core\_python.core.component.abstract\_loader module +-------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.abstract_loader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.component.tactics.rst b/docs/source/adf_core_python/adf_core_python.core.component.tactics.rst new file mode 100644 index 00000000..d640551d --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.component.tactics.rst @@ -0,0 +1,77 @@ +adf\_core\_python.core.component.tactics package +================================================ + +Submodules +---------- + +adf\_core\_python.core.component.tactics.tactics\_agent module +-------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_agent + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_ambulance\_center module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_ambulance_center + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_ambulance\_team module +------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.component.tactics.tactics_ambulance_team + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_center module +--------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_center + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_fire\_brigade module +---------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_fire_brigade + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_fire\_station module +---------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_fire_station + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_police\_force module +---------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_police_force + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.component.tactics.tactics\_police\_office module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.component.tactics.tactics_police_office + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.component.tactics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.config.rst b/docs/source/adf_core_python/adf_core_python.core.config.rst new file mode 100644 index 00000000..5806b37c --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.config.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.config package +===================================== + +Submodules +---------- + +adf\_core\_python.core.config.config module +------------------------------------------- + +.. automodule:: adf_core_python.core.config.config + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.config + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.algorithm.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.algorithm.rst new file mode 100644 index 00000000..2197aef2 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.algorithm.rst @@ -0,0 +1,29 @@ +adf\_core\_python.core.gateway.component.module.algorithm package +================================================================= + +Submodules +---------- + +adf\_core\_python.core.gateway.component.module.algorithm.gateway\_clustering module +------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.gateway.component.module.algorithm.gateway_clustering + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.algorithm.gateway\_path\_planning module +---------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.algorithm.gateway_path_planning + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.component.module.algorithm + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.complex.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.complex.rst new file mode 100644 index 00000000..819f4acf --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.complex.rst @@ -0,0 +1,77 @@ +adf\_core\_python.core.gateway.component.module.complex package +=============================================================== + +Submodules +---------- + +adf\_core\_python.core.gateway.component.module.complex.gateway\_ambulance\_target\_allocator module +---------------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_ambulance_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_fire\_target\_allocator module +----------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_fire_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_human\_detector module +--------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_human_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_police\_target\_allocator module +------------------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_police_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_road\_detector module +-------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_road_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_search module +------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_search + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_target\_allocator module +----------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.component.module.complex.gateway\_target\_detector module +---------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex.gateway_target_detector + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.component.module.complex + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.rst new file mode 100644 index 00000000..4d69a966 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.component.module.rst @@ -0,0 +1,30 @@ +adf\_core\_python.core.gateway.component.module package +======================================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.gateway.component.module.algorithm + adf_core_python.core.gateway.component.module.complex + +Submodules +---------- + +adf\_core\_python.core.gateway.component.module.gateway\_abstract\_module module +-------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.component.module.gateway_abstract_module + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.component.module + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.component.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.component.rst new file mode 100644 index 00000000..29a66e8c --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.component.rst @@ -0,0 +1,18 @@ +adf\_core\_python.core.gateway.component package +================================================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.gateway.component.module + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.component + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.message.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.message.rst new file mode 100644 index 00000000..265575ac --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.message.rst @@ -0,0 +1,77 @@ +adf\_core\_python.core.gateway.message package +============================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.gateway.message.urn + +Submodules +---------- + +adf\_core\_python.core.gateway.message.am\_agent module +------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.message.am_agent + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.am\_exec module +------------------------------------------------------ + +.. automodule:: adf_core_python.core.gateway.message.am_exec + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.am\_module module +-------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.message.am_module + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.am\_update module +-------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.message.am_update + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.ma\_exec\_response module +---------------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.message.ma_exec_response + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.ma\_module\_response module +------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.gateway.message.ma_module_response + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.message.moduleMessageFactory module +------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.gateway.message.moduleMessageFactory + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.message + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.message.urn.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.message.urn.rst new file mode 100644 index 00000000..fa3e1e85 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.message.urn.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.gateway.message.urn package +================================================== + +Submodules +---------- + +adf\_core\_python.core.gateway.message.urn.urn module +----------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.message.urn.urn + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway.message.urn + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.gateway.rst b/docs/source/adf_core_python/adf_core_python.core.gateway.rst new file mode 100644 index 00000000..ca47556e --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.gateway.rst @@ -0,0 +1,54 @@ +adf\_core\_python.core.gateway package +====================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.gateway.component + adf_core_python.core.gateway.message + +Submodules +---------- + +adf\_core\_python.core.gateway.gateway\_agent module +---------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.gateway_agent + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.gateway\_launcher module +------------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.gateway_launcher + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.gateway\_module module +----------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.gateway_module + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.gateway.module\_dict module +-------------------------------------------------- + +.. automodule:: adf_core_python.core.gateway.module_dict + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.gateway + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.launcher.connect.rst b/docs/source/adf_core_python/adf_core_python.core.launcher.connect.rst new file mode 100644 index 00000000..63501ff1 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.launcher.connect.rst @@ -0,0 +1,85 @@ +adf\_core\_python.core.launcher.connect package +=============================================== + +Submodules +---------- + +adf\_core\_python.core.launcher.connect.component\_launcher module +------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.launcher.connect.component_launcher + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connection module +--------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connection + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector module +-------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_ambulance\_center module +--------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector_ambulance_center + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_ambulance\_team module +------------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector_ambulance_team + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_fire\_brigade module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector_fire_brigade + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_fire\_station module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector_fire_station + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_police\_force module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.connect.connector_police_force + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.connect.connector\_police\_office module +------------------------------------------------------------------------ + +.. automodule:: adf_core_python.core.launcher.connect.connector_police_office + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.launcher.connect + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.launcher.rst b/docs/source/adf_core_python/adf_core_python.core.launcher.rst new file mode 100644 index 00000000..e93773be --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.launcher.rst @@ -0,0 +1,37 @@ +adf\_core\_python.core.launcher package +======================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.launcher.connect + +Submodules +---------- + +adf\_core\_python.core.launcher.agent\_launcher module +------------------------------------------------------ + +.. automodule:: adf_core_python.core.launcher.agent_launcher + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.core.launcher.config\_key module +-------------------------------------------------- + +.. automodule:: adf_core_python.core.launcher.config_key + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.launcher + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.logger.rst b/docs/source/adf_core_python/adf_core_python.core.logger.rst new file mode 100644 index 00000000..a0bf1f5b --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.logger.rst @@ -0,0 +1,21 @@ +adf\_core\_python.core.logger package +===================================== + +Submodules +---------- + +adf\_core\_python.core.logger.logger module +------------------------------------------- + +.. automodule:: adf_core_python.core.logger.logger + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.core.logger + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.core.rst b/docs/source/adf_core_python/adf_core_python.core.rst new file mode 100644 index 00000000..78f49554 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.core.rst @@ -0,0 +1,23 @@ +adf\_core\_python.core package +============================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.core.agent + adf_core_python.core.component + adf_core_python.core.config + adf_core_python.core.gateway + adf_core_python.core.launcher + adf_core_python.core.logger + +Module contents +--------------- + +.. automodule:: adf_core_python.core + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.action.rst b/docs/source/adf_core_python/adf_core_python.implement.action.rst new file mode 100644 index 00000000..e26c2e67 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.action.rst @@ -0,0 +1,45 @@ +adf\_core\_python.implement.action package +========================================== + +Submodules +---------- + +adf\_core\_python.implement.action.default\_extend\_action\_clear module +------------------------------------------------------------------------ + +.. automodule:: adf_core_python.implement.action.default_extend_action_clear + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.action.default\_extend\_action\_move module +----------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.action.default_extend_action_move + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.action.default\_extend\_action\_rescue module +------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.action.default_extend_action_rescue + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.action.default\_extend\_action\_transport module +---------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.action.default_extend_action_transport + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.action + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.module.algorithm.rst b/docs/source/adf_core_python/adf_core_python.implement.module.algorithm.rst new file mode 100644 index 00000000..ce3486d2 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.module.algorithm.rst @@ -0,0 +1,37 @@ +adf\_core\_python.implement.module.algorithm package +==================================================== + +Submodules +---------- + +adf\_core\_python.implement.module.algorithm.a\_star\_path\_planning module +--------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.algorithm.a_star_path_planning + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.algorithm.dijkstra\_path\_planning module +---------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.algorithm.dijkstra_path_planning + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.algorithm.k\_means\_clustering module +------------------------------------------------------------------------ + +.. automodule:: adf_core_python.implement.module.algorithm.k_means_clustering + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.module.algorithm + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.module.communication.rst b/docs/source/adf_core_python/adf_core_python.implement.module.communication.rst new file mode 100644 index 00000000..bca31007 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.module.communication.rst @@ -0,0 +1,29 @@ +adf\_core\_python.implement.module.communication package +======================================================== + +Submodules +---------- + +adf\_core\_python.implement.module.communication.default\_channel\_subscriber module +------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.implement.module.communication.default_channel_subscriber + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.communication.default\_message\_coordinator module +------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.communication.default_message_coordinator + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.module.communication + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.module.complex.rst b/docs/source/adf_core_python/adf_core_python.implement.module.complex.rst new file mode 100644 index 00000000..7c28aa18 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.module.complex.rst @@ -0,0 +1,61 @@ +adf\_core\_python.implement.module.complex package +================================================== + +Submodules +---------- + +adf\_core\_python.implement.module.complex.default\_ambulance\_target\_allocator module +--------------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.complex.default_ambulance_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.complex.default\_fire\_target\_allocator module +---------------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.complex.default_fire_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.complex.default\_human\_detector module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.complex.default_human_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.complex.default\_police\_target\_allocator module +------------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.implement.module.complex.default_police_target_allocator + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.complex.default\_road\_detector module +------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.complex.default_road_detector + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.module.complex.default\_search module +----------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.module.complex.default_search + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.module.complex + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.module.rst b/docs/source/adf_core_python/adf_core_python.implement.module.rst new file mode 100644 index 00000000..12058ced --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.module.rst @@ -0,0 +1,20 @@ +adf\_core\_python.implement.module package +========================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.implement.module.algorithm + adf_core_python.implement.module.communication + adf_core_python.implement.module.complex + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.module + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.rst b/docs/source/adf_core_python/adf_core_python.implement.rst new file mode 100644 index 00000000..83e213ce --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.rst @@ -0,0 +1,31 @@ +adf\_core\_python.implement package +=================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.implement.action + adf_core_python.implement.module + adf_core_python.implement.tactics + +Submodules +---------- + +adf\_core\_python.implement.default\_loader module +-------------------------------------------------- + +.. automodule:: adf_core_python.implement.default_loader + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.implement.tactics.rst b/docs/source/adf_core_python/adf_core_python.implement.tactics.rst new file mode 100644 index 00000000..8076aea9 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.implement.tactics.rst @@ -0,0 +1,61 @@ +adf\_core\_python.implement.tactics package +=========================================== + +Submodules +---------- + +adf\_core\_python.implement.tactics.default\_tactics\_ambulance\_center module +------------------------------------------------------------------------------ + +.. automodule:: adf_core_python.implement.tactics.default_tactics_ambulance_center + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.tactics.default\_tactics\_ambulance\_team module +---------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.tactics.default_tactics_ambulance_team + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.tactics.default\_tactics\_fire\_brigade module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.tactics.default_tactics_fire_brigade + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.tactics.default\_tactics\_fire\_station module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.tactics.default_tactics_fire_station + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.tactics.default\_tactics\_police\_force module +-------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.tactics.default_tactics_police_force + :members: + :undoc-members: + :show-inheritance: + +adf\_core\_python.implement.tactics.default\_tactics\_police\_office module +--------------------------------------------------------------------------- + +.. automodule:: adf_core_python.implement.tactics.default_tactics_police_office + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python.implement.tactics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/adf_core_python.rst b/docs/source/adf_core_python/adf_core_python.rst new file mode 100644 index 00000000..774319a2 --- /dev/null +++ b/docs/source/adf_core_python/adf_core_python.rst @@ -0,0 +1,31 @@ +adf\_core\_python package +========================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + adf_core_python.cli + adf_core_python.core + adf_core_python.implement + +Submodules +---------- + +adf\_core\_python.launcher module +--------------------------------- + +.. automodule:: adf_core_python.launcher + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: adf_core_python + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/adf_core_python/modules.rst b/docs/source/adf_core_python/modules.rst new file mode 100644 index 00000000..d0160b5d --- /dev/null +++ b/docs/source/adf_core_python/modules.rst @@ -0,0 +1,7 @@ +adf_core_python +=============== + +.. toctree:: + :maxdepth: 4 + + adf_core_python diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..fc0ea646 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,92 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +import os +import sys +import yaml + +sys.path.insert(0, os.path.abspath("../../")) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "adf-core-python" +copyright = "2024, Haruki Uehara, Yuki Shimada" +author = "Haruki Uehara, Yuki Shimada" +release = "0.1.0" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.autosummary", + "sphinx_copybutton", + "sphinx_togglebutton", + "myst_parser", + "sphinxcontrib.mermaid", +] + +templates_path = ["_templates"] +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_book_theme" + +html_sidebars = { + "**": ["navbar-logo.html", "versions.html", "search-button-field.html", "sbt-sidebar-nav.html"] +} + +html_static_path = ["_static"] + +locale_dirs = ["locale"] +language = "en" + +# get the environment variable build_all_docs and pages_root +build_all_docs = os.environ.get("build_all_docs") +pages_root = os.environ.get("pages_root", "") + +# if not there, we dont call this +if build_all_docs is not None: + # we get the current language and version + current_language = os.environ.get("current_language") + current_version = os.environ.get("current_version") + + # we set the html_context wit current language and version + # and empty languages and versions for now + html_context = { + 'current_language' : current_language, + 'languages' : [], + 'current_version' : current_version, + 'versions' : [], + } + + + # and we append all versions and langauges accordingly + # we treat t he main branch as latest + if (current_version == 'latest'): + html_context['languages'].append(['en', pages_root]) + html_context['languages'].append(['ja', pages_root+'/ja']) + + if (current_language == 'en'): + html_context['versions'].append(['latest', pages_root]) + if (current_language == 'ja'): + html_context['versions'].append(['latest', pages_root+'/ja']) + + # and loop over all other versions from our yaml file + # to set versions and languages + with open("../versions.yaml", "r") as yaml_file: + docs = yaml.safe_load(yaml_file) + + if (current_version != 'latest'): + for language in docs[current_version].get('languages', []): + html_context['languages'].append([language, pages_root+'/'+current_version+'/'+language]) + + for version, details in docs.items(): + html_context['versions'].append([version, pages_root+'/'+version+'/'+current_language]) diff --git a/docs/source/download/cluster_plot.zip b/docs/source/download/cluster_plot.zip new file mode 100644 index 00000000..122b555b Binary files /dev/null and b/docs/source/download/cluster_plot.zip differ diff --git a/docs/source/download/tutorial_map.zip b/docs/source/download/tutorial_map.zip new file mode 100644 index 00000000..429d2fea Binary files /dev/null and b/docs/source/download/tutorial_map.zip differ diff --git a/docs/source/hands-on/clustering.md b/docs/source/hands-on/clustering.md new file mode 100644 index 00000000..df3962ab --- /dev/null +++ b/docs/source/hands-on/clustering.md @@ -0,0 +1,362 @@ +# クラスタリングモジュール + +## クラスタリングモジュールの目的 + +複数のエージェントを動かす場合は、それらのエージェントにどのように協調させるかが重要になります。RRSでは多くのチームが、エージェントに各々の担当地域を持たせ役割分担をおこなう協調を取り入れています(他の手段による協調も取り入れています)。担当地域を割り振るためには、地図上のオブジェクトをいくつかのグループに分ける必要があります。このようなグループ分けをしてそれらを管理する場合には、クラスタリングモジュールと呼ばれるモジュールを用います。 + +本資料では、多くの世界大会参加チームが使用しているアルゴリズムを用いたクラスタリングモジュールの実装をおこないます。 + +## 開発するクラスタリングモジュールの概要 + +本資料で開発するモジュールは下の画像のように、 + +1. k-means++アルゴリズムによって地図上のオブジェクトをエージェント数分の区画に分けます。 +1. Hungarianアルゴリズムによってそれらの区画とエージェントを (間の距離の総和が最も小さくなるように)1対1で結びつけます。 + +![クラスタリングの画像](./../images/clustering_image.jpg) + +## クラスタリングモジュールの実装 + +```{note} +以降の作業では、カレントディレクトリがプロジェクトのルートディレクトリであることを前提としています。 +``` + +まず、クラスタリングモジュールを記述するためのファイルを作成します。 + +```bash +mkdir -p src//module/algorithm +touch src//module/algorithm/k_means_pp_clustering.py +``` + +次に、クラスタリングモジュールの実装を行います。 以下のコードを `k_means_pp_clustering.py` に記述してください。 + +```python +import numpy as np +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, ScenarioInfoKeys +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.logger.logger import get_logger +from rcrs_core.connection.URN import Entity as EntityURN +from rcrs_core.entities.ambulanceCenter import AmbulanceCentre +from rcrs_core.entities.building import Building +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.fireStation import FireStation +from rcrs_core.entities.gasStation import GasStation +from rcrs_core.entities.hydrant import Hydrant +from rcrs_core.entities.policeOffice import PoliceOffice +from rcrs_core.entities.refuge import Refuge +from rcrs_core.entities.road import Road +from rcrs_core.worldmodel.entityID import EntityID +from scipy.optimize import linear_sum_assignment +from sklearn.cluster import KMeans + +# クラスタリングのシード値 +SEED = 42 + + +class KMeansPPClustering(Clustering): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + # ロガーの取得 + self._logger = get_logger(f"{self.__class__.__name__}") + + # クラスター数の設定 + self._cluster_number: int = 1 + match agent_info.get_myself().get_urn(): + # エージェントのクラスに応じてクラスター数を設定 + case EntityURN.AMBULANCE_TEAM: + self._cluster_number = scenario_info.get_value( + ScenarioInfoKeys.SCENARIO_AGENTS_AT, + 1, + ) + case EntityURN.POLICE_FORCE: + self._cluster_number = scenario_info.get_value( + ScenarioInfoKeys.SCENARIO_AGENTS_PF, + 1, + ) + case EntityURN.FIRE_BRIGADE: + self._cluster_number = scenario_info.get_value( + ScenarioInfoKeys.SCENARIO_AGENTS_FB, + 1, + ) + + # 自分と同じクラスのエージェントのリストを取得 + self._agents: list[Entity] = world_info.get_entities_of_types( + [ + agent_info.get_myself().__class__, + ] + ) + + # クラスタリング結果を保持する変数 + self._cluster_entities: list[list[Entity]] = [] + + # クラスタリング対象のエンティティのリストを取得 + self._entities: list[Entity] = world_info.get_entities_of_types( + [ + AmbulanceCentre, + FireStation, + GasStation, + Hydrant, + PoliceOffice, + Refuge, + Road, + Building, + ] + ) + + def calculate(self) -> Clustering: + return self + + def get_cluster_number(self) -> int: + """ + クラスター数を取得する + + Returns + ------- + int + クラスター数 + """ + return self._cluster_number + + def get_cluster_index(self, entity_id: EntityID) -> int: + """ + エージェントに割り当てられたクラスターのインデックスを取得する + + Parameters + ---------- + entity_id : EntityID + エージェントのID + + Returns + ------- + int + クラスターのインデックス + """ + return self._agent_cluster_indices.get(entity_id, 0) + + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + """ + クラスターのエンティティのリストを取得する + + Parameters + ---------- + cluster_index : int + クラスターのインデックス + + Returns + ------- + list[Entity] + クラスターのエンティティのリスト + """ + if cluster_index >= len(self._cluster_entities): + return [] + return self._cluster_entities[cluster_index] + + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + """ + クラスターのエンティティのIDのリストを取得する + + Parameters + ---------- + cluster_index : int + クラスターのインデックス + + Returns + ------- + list[EntityID] + クラスターのエンティティのIDのリスト + """ + if cluster_index >= len(self._cluster_entities): + return [] + return [entity.get_id() for entity in self._cluster_entities[cluster_index]] + + def prepare(self) -> Clustering: + """ + エージェントの起動時に一回のみ実行される処理 + """ + super().prepare() + if self.get_count_prepare() > 1: + return self + + # クラスタリングを実行 + kmeans_pp = self._perform_kmeans_pp(self._entities, self._cluster_number) + + # クラスタリング結果を保持 + self._cluster_entities = [[] for _ in range(self._cluster_number)] + for entity, cluster_index in zip(self._entities, kmeans_pp.labels_): + self._cluster_entities[cluster_index].append(entity) + + # エージェントとクラスターのエンティティの距離を計算し、最も全体の合計の距離が短くなるようにエージェントとクラスターを対応付ける + agent_cluster_indices = self._agent_cluster_assignment( + self._agents, kmeans_pp.cluster_centers_ + ) + + # エージェントとクラスターの対応付け結果を保持 + self._agent_cluster_indices = { + entity.get_id(): cluster_index + for entity, cluster_index in zip(self._agents, agent_cluster_indices) + } + + # デバッグ用のログ出力 + self._logger.info( + f"Clustered entities: {[[entity.get_id().get_value() for entity in cluster] for cluster in self._cluster_entities]}" + ) + + self._logger.info( + f"Agent cluster indices: {[([self._world_info.get_entity(entity_id).get_x(), self._world_info.get_entity(entity_id).get_y()], int(cluster_index)) for entity_id, cluster_index in self._agent_cluster_indices.items()]}" + ) + + return self + + def _perform_kmeans_pp(self, entities: list[Entity], n_clusters: int = 1) -> KMeans: + """ + K-means++法によるクラスタリングを実行する + + Parameters + ---------- + entities : list[Entity] + クラスタリング対象のエンティティのリスト + + n_clusters : int, optional + クラスター数, by default 1 + + Returns + ------- + KMeans + クラスタリング結果 + """ + entity_positions: np.ndarray = np.array( + [ + [entity.get_x(), entity.get_y()] + for entity in entities + if entity.get_x() is not None and entity.get_y() is not None + ] + ) + + entity_positions = entity_positions.reshape(-1, 2) + kmeans_pp = KMeans( + n_clusters=n_clusters, + init="k-means++", + random_state=SEED, + ) + kmeans_pp.fit(entity_positions) + return kmeans_pp + + def _agent_cluster_assignment( + self, agents: list[Entity], cluster_positions: np.ndarray + ) -> np.ndarray: + """ + エージェントとクラスターの対応付けを行う + + Parameters + ---------- + agents : list[Entity] + エージェントのリスト + + cluster_positions : np.ndarray + クラスターの位置のリスト + + Returns + ------- + np.ndarray + エージェントとクラスターの対応付け結果 + """ + # エージェントの位置のリストを取得 + agent_positions = np.array( + [ + [agent.get_x(), agent.get_y()] + for agent in agents + if agent.get_x() is not None and agent.get_y() is not None + ] + ) + + # エージェントとクラスターの距離行列を計算 + agent_positions = agent_positions.reshape(-1, 2) + cost_matrix = np.linalg.norm( + agent_positions[:, np.newaxis] - cluster_positions, axis=2 + ) + # ハンガリアンアルゴリズムによりエージェントとクラスターの対応付けを行う + _, col_ind = linear_sum_assignment(cost_matrix) + return col_ind +``` + +k-means++の実装は、scikit-learnの`KMeans`クラスを使用しています。`KMeans`クラスは、`n_clusters`で指定したクラスター数によって地図上のオブジェクトをクラスタリングします。クラスタリング結果は、`labels_`属性に格納されます。また、`cluster_centers_`属性には各クラスターの中心座標が格納されます。 + +hungarianアルゴリズムの実装は、scipyの`linear_sum_assignment`関数を使用しています。`linear_sum_assignment`関数は、コスト行列を引数として受け取り、最適な割り当てを行います。 + +次に、作成したモジュールを登録します。`config/module.yaml` を以下のように編集してください。 + +```yaml +SampleSearch: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + Clustering: src..module.algorithm.k_means_pp_clustering.KMeansPPClustering + +SampleHumanDetector: + Clustering: src..module.algorithm.k_means_pp_clustering.KMeansPPClustering +``` + +ターミナルを2つ起動します。 + +片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します: + +```bash +# Terminal A +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_ambulance_team_only/map -c ../maps/tutorial_ambulance_team_only/config +``` + +その後、別のターミナルを開き、エージェントを起動します: + +```bash +# Terminal B +cd WORKING_DIR/ +python main.py +``` + +エージェントが起動すると、標準出力にクラスタリング結果が表示されます。 + +```bash +[info ] Clustered entities: [[257, 259, 262, 263, 270, 278, 280, 297, 336, 913, 914, 915, 916, 917, 918, 919, 933, 941, 942, 943, 944, 945, 946, 947, 974, 250, 253], [349, 896, 899, 902, 934, 960, 968, 969, 970, 971, 248, 251], [258, 266, 268, 269, 274, 275, 279, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 932, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 975, 976, 254, 255], [256, 271, 273, 281, 296, 298, 314, 330, 903, 904, 905, 910, 911, 912, 935, 936, 937, 938, 939, 940, 247, 249]] [KMeansPPClustering] +[info ] Agent cluster indices: [([89544, 19925], 1), ([69989, 120063], 0), ([130029, 50380], 2), ([29898, 59056], 3)] [KMeansPPClustering] +``` + +このままだと、クラスタリング結果がわかりにくいので、クラスタリング結果を地図上に表示してみましょう。 + +{download}`クラスターの可視化用スクリプト <./../download/cluster_plot.zip>`をダウンロードして解凍し、中の`main.py`の以下の部分に + +```python +# クラスタリング結果 +clusters = [] +``` + +出力の`Clustered entities: `の後ろの部分の配列をコピーして貼り付けてください。 + +例 + +```python +# クラスタリング結果 +clusters = [[257, 259, 262, 263, 270, 278, 280, 297, 336, 913, 914, 915, 916, 917, 918, 919, 933, 941, 942, 943, 944, 945, 946, 947, 974, 250, 253], [349, 896, 899, 902, 934, 960, 968, 969, 970, 971, 248, 251], [258, 266, 268, 269, 274, 275, 279, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 932, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 975, 976, 254, 255], [256, 271, 273, 281, 296, 298, 314, 330, 903, 904, 905, 910, 911, 912, 935, 936, 937, 938, 939, 940, 247, 249]] +``` + +貼り付けたら、以下のコマンドを実行してください。 + +```bash +pip install -r requirements.txt +python main.py +``` + +以下のような画像が出力されます。 + +![クラスタリングの画像](./../images/cluster.png) diff --git a/docs/source/hands-on/search.md b/docs/source/hands-on/search.md new file mode 100644 index 00000000..8bd4ba50 --- /dev/null +++ b/docs/source/hands-on/search.md @@ -0,0 +1,383 @@ +# サーチモジュール + +## サーチモジュールの概要 + +今回開発するモジュールは、`KMeansPPClustering` モジュールを用いた情報探索対象決定 (`Search`) モジュールです。 クラスタリングモジュールによってエージェント間で担当地域の分割をおこない、 担当地域内からランダムに探索対象として選択します。 + +## サーチモジュールの実装の準備 + +```{note} +以降の作業では、カレントディレクトリがプロジェクトのルートディレクトリであることを前提としています。 +``` + +まず、サーチモジュールを記述するためのファイルを作成します。 + +```bash +touch src//module/complex/k_means_pp_search.py +``` + +次に、サーチモジュールの実装を行います。 以下のコードを `k_means_pp_search.py` に記述してください。 +これが今回実装するサーチモジュールの雛形になります。 + +```python +import random +from typing import Optional, cast + +from rcrs_core.entities.building import Building +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.refuge import Refuge +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.logger.logger import get_agent_logger + + +class KMeansPPSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._result: Optional[EntityID] = None + # ロガーの取得 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def calculate(self) -> Search: + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result +``` + +## モジュールの登録 + +次に、作成したモジュールを登録します。 +以下のように`config/module.yaml`の該当箇所を変更してください + +```yaml +DefaultTacticsAmbulanceTeam: + Search: src..module.complex.k_means_pp_search.KMeansPPSearch + +DefaultTacticsFireBrigade: + Search: src..module.complex.k_means_pp_search.KMeansPPSearch + +DefaultTacticsPoliceForce: + Search: src..module.complex.k_means_pp_search.KMeansPPSearch +``` + +## モジュールの実装 + +まず、`KMeansPPClustering` モジュールを呼び出せるようにします。 + +以下のコードを`config/module.yaml`に追記してください。 + +```yaml +KMeansPPSearch: + Clustering: src..module.algorithm.k_means_pp_clustering.KMeansPPClustering +``` + +次に、`KMeansPPSearch` モジュールで `KMeansPPClustering` モジュールを呼び出せるようにします。 + +以下のコードを `k_means_pp_search.py` に追記してください。 + +```python +class KMeansPPSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._result: Optional[EntityID] = None + + # ロガーの取得 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + # クラスタリングモジュールの読み込み + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + # config.yamlに登録したkey + "KMeansPPSearch.Clustering", + # 上記のkeyが登録されていなかった場合のデフォルトモジュール + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + # クラスタリングモジュールの登録 + self.register_sub_module(self._clustering) +``` + +そして、`calculate` メソッドでクラスタリングモジュールを呼び出し、探索対象を決定するように変更します。 + +以下のコードを `k_means_pp_search.py` に追記してください。 + +```python + def calculate(self) -> Search: + # 自エージェントのエンティティIDを取得 + me: EntityID = self._agent_info.get_entity_id() + # 自エージェントが所属するクラスターのインデックスを取得 + allocated_cluster_index: int = self._clustering.get_cluster_index(me) + # クラスター内のエンティティIDを取得 + cluster_entity_ids: list[EntityID] = self._clustering.get_cluster_entity_ids( + allocated_cluster_index + ) + # 乱数で選択 + if cluster_entity_ids: + self._result = random.choice(cluster_entity_ids) + + # ログ出力 + self._logger.info(f"Target entity ID: {self._result}") + + return self +``` + +以上で、`KMeansPPClustering` モジュールを用いた `KMeansPPSearch` モジュールの実装が完了しました。 + +ターミナルを2つ起動します。 + +片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します: + +```bash +# Terminal A +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_ambulance_team_only/map -c ../maps/tutorial_ambulance_team_only/config +``` + +その後、別のターミナルを開き、エージェントを起動します: + +```bash +# Terminal B +cd WORKING_DIR/ +python main.py +``` + +## モジュールの改善 + +`KMeansPPSearch` モジュールは、クラスタリングモジュールを用いて担当地域内からランダムに探索対象を選択しています。 +そのため、以下のような問題があります。 + +- 探索対象がステップごとに変わってしまう + - 目標にたどり着く前に探索対象が変わってしまうため、なかなか目標にたどり着けない + - 色んなところにランダムに探索対象を選択することで、効率的な探索ができない +- すでに探索したエンティティを再度探索対象として選択してしまうため、効率的な探索ができない +- 近くに未探索のエンティティがあるのに、遠くのエンティティを探索対象として選択してしまう + +などの問題があります。 + +## 課題 + +`KMeansPPSearch` モジュールを改善し、より効率的な探索を行うモジュールを実装して見てください。 + +```{warning} +ここに上げた問題以外にも、改善すべき点が存在すると思うので、それを改善していただいても構いません。 +``` + +```{warning} +プログラム例のプログラムにも一部改善点があるので、余裕があったら修正してみてください。 +``` + +### 探索対象がステップごとに変わってしまう問題 + +```{admonition} 方針のヒント +:class: hint dropdown + +一度選択した探索対象に到達するまで、探索対象を変更しないようにする +``` + +```{admonition} プログラム例 +:class: hint dropdown + +````python + def calculate(self) -> Search: + # 自エージェントのエンティティIDを取得 + me: EntityID = self._agent_info.get_entity_id() + # 自エージェントが所属するクラスターのインデックスを取得 + allocated_cluster_index: int = self._clustering.get_cluster_index(me) + # クラスター内のエンティティIDを取得 + cluster_entity_ids: list[EntityID] = self._clustering.get_cluster_entity_ids( + allocated_cluster_index + ) + + # 探索対象をすでに選んでいる場合 + if self._result: + # 自エージェントのいる場所のエンティティIDを取得 + my_position = self._agent_info.get_position_entity_id() + # 探索対象の場所のエンティティIDを取得 + target_position = self._world_info.get_entity_position(self._result) + # 自エージェントのいる場所と探索対象の場所が一致している場合、探索対象をリセット + if my_position == target_position: + # 探索対象をリセット + self._result = None + + # 探索対象が未選択の場合 + if not self._result and cluster_entity_ids: + self._result = random.choice(cluster_entity_ids) + + # ログ出力 + self._logger.info(f"Target entity ID: {self._result}") + + return self +``` + +### すでに探索したエンティティを再度探索対象として選択してしまう問題 + +```{admonition} 方針のヒント +:class: hint dropdown + +すでに探索したエンティティを何かしらの方法で記録し、再度探索対象として選択しないようにする +``` + +```{admonition} プログラム例 +:class: hint dropdown + +````python + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._result: Optional[EntityID] = None + + # ロガーの取得 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + # クラスタリングモジュールの読み込み + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + # config.yamlに登録したkey + "KMeansPPSearch.Clustering", + # 上記のkeyが登録されていなかった場合のデフォルトモジュール + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + # クラスタリングモジュールの要録 + self.register_sub_module(self._clustering) + + # 探索したいエンティティIDのリスト(追加) + self._search_entity_ids: list[EntityID] = [] + + def calculate(self) -> Search: + # 探索したいエンティティIDのリストが空の場合 + if not self._search_entity_ids: + # 自エージェントのエンティティIDを取得 + me: EntityID = self._agent_info.get_entity_id() + # 自エージェントが所属するクラスターのインデックスを取得 + allocated_cluster_index: int = self._clustering.get_cluster_index(me) + # クラスター内のエンティティIDを取得(変更) + self._search_entity_ids: list[EntityID] = ( + self._clustering.get_cluster_entity_ids(allocated_cluster_index) + ) + + # 探索対象をすでに選んでいる場合 + if self._result: + # 自エージェントのいる場所のエンティティIDを取得 + my_position = self._agent_info.get_position_entity_id() + # 探索対象の場所のエンティティIDを取得 + target_position = self._world_info.get_entity_position(self._result) + # 自エージェントのいる場所と探索対象の場所が一致している場合、探索対象をリセット + if my_position == target_position: + # 探索したいエンティティIDのリストから探索対象を削除 + self._search_entity_ids.remove(self._result) + # 探索対象をリセット + self._result = None + + # 探索対象が未選択の場合(変更) + if not self._result and self._search_entity_ids: + self._result = random.choice(self._search_entity_ids) + + # ログ出力 + self._logger.info(f"Target entity ID: {self._result}") + + return self +``` + +### 近くに未探索のエンティティがあるのに、遠くのエンティティを探索対象として選択してしまう + +```{admonition} 方針のヒント +:class: hint dropdown + +エンティティ間の距離を計算し、もっとも近いエンティティを探索対象として選択する +``` + +```{admonition} プログラム例 +:class: hint dropdown + +````python + def calculate(self) -> Search: + # 探索したいエンティティIDのリストが空の場合 + if not self._search_entity_ids: + # 自エージェントのエンティティIDを取得 + me: EntityID = self._agent_info.get_entity_id() + # 自エージェントが所属するクラスターのインデックスを取得 + allocated_cluster_index: int = self._clustering.get_cluster_index(me) + # クラスター内のエンティティIDを取得 + self._search_entity_ids: list[EntityID] = ( + self._clustering.get_cluster_entity_ids(allocated_cluster_index) + ) + + # 探索対象をすでに選んでいる場合 + if self._result: + # 自エージェントのいる場所のエンティティIDを取得 + my_position = self._agent_info.get_position_entity_id() + # 探索対象の場所のエンティティIDを取得 + target_position = self._world_info.get_entity_position(self._result) + # 自エージェントのいる場所と探索対象の場所が一致している場合、探索対象をリセット + if my_position == target_position: + # 探索したいエンティティIDのリストから探索対象を削除 + self._search_entity_ids.remove(self._result) + # 探索対象をリセット + self._result = None + + # 探索対象が未選択の場合 + if not self._result and self._search_entity_ids: + nearest_entity_id: Optional[EntityID] = None + nearest_distance: float = float("inf") + me: EntityID = self._agent_info.get_entity_id() + # 探索対象の中で自エージェントに最も近いエンティティIDを選択(変更) + for entity_id in self._search_entity_ids: + distance = self._world_info.get_distance(me, entity_id) + if distance < nearest_distance: + nearest_entity_id = entity_id + nearest_distance = distance + self._result = nearest_entity_id + + # ログ出力 + self._logger.info(f"Target entity ID: {self._result}") + + return self +``` diff --git a/docs/source/images/agent_control.jpg b/docs/source/images/agent_control.jpg new file mode 100644 index 00000000..a641c8d0 Binary files /dev/null and b/docs/source/images/agent_control.jpg differ diff --git a/docs/source/images/agent_flow.png b/docs/source/images/agent_flow.png new file mode 100644 index 00000000..fb5720f5 Binary files /dev/null and b/docs/source/images/agent_flow.png differ diff --git a/docs/source/images/cluster.png b/docs/source/images/cluster.png new file mode 100644 index 00000000..4e0b9fb8 Binary files /dev/null and b/docs/source/images/cluster.png differ diff --git a/docs/source/images/clustering_image.jpg b/docs/source/images/clustering_image.jpg new file mode 100644 index 00000000..6fb37ebd Binary files /dev/null and b/docs/source/images/clustering_image.jpg differ diff --git a/docs/source/images/entity.png b/docs/source/images/entity.png new file mode 100644 index 00000000..503c34cf Binary files /dev/null and b/docs/source/images/entity.png differ diff --git a/docs/source/images/human_detector_flow.png b/docs/source/images/human_detector_flow.png new file mode 100644 index 00000000..13856ce9 Binary files /dev/null and b/docs/source/images/human_detector_flow.png differ diff --git a/docs/source/images/launch_server.png b/docs/source/images/launch_server.png new file mode 100644 index 00000000..2b60ab33 Binary files /dev/null and b/docs/source/images/launch_server.png differ diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..e10fb8d4 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,80 @@ +.. adf-core-python documentation master file, created by + sphinx-quickstart on Mon Oct 14 00:15:07 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +adf-core-pythonのドキュメント +============================== + +.. warning:: + + 現在このパッケージは開発中です。破壊的な変更が行われる可能性があります。 + +.. warning:: + + パッケージとしてまだ公開していないため、pip でインストールすることはできません。 + +.. warning:: + + 以下の言語のドキュメントは機械翻訳を使用しています。翻訳の正確性については保証できません。 + 英語 + + +概要 +---- +adf-core-pythonは、RoboCup Rescue Simulation(RRS)におけるエージェント開発を支援するためのライブラリ及びフレームワークです。 +adf-core-pythonを使用することで、エージェントの開発を効率化し、再利用性を向上させることができます。 + +特徴 +---- +adf-core-pythonには以下のような特徴があります。 + +- **モジュール単位での開発**: モジュール単位でエージェント開発を行い、モジュールの入れ替えが容易です。 +- **モジュールの再利用**: 他のエージェントで使用されているモジュールを再利用することができます。 +- **エージェントの開発に集中**: シミュレーションサーバーとの通信やログ出力などの共通処理をライブラリが提供します。 + +はじめに +-------- +adf-core-pythonを始めるには、インストールに従い、このドキュメントに記載されているチュートリアルやハンズオンを参照してください。 + +.. toctree:: + :maxdepth: 1 + :caption: インストール + + install/environment/environment + install/install/install + +.. toctree:: + :maxdepth: 1 + :caption: クイックスタート + + quickstart/quickstart + +.. toctree:: + :maxdepth: 1 + :caption: チュートリアル + + tutorial/environment/environment + tutorial/agent/agent + tutorial/agent/agent_control + tutorial/config/config + tutorial/module/module + +.. toctree:: + :maxdepth: 1 + :caption: ハンズオン + + hands-on/clustering + hands-on/search + +.. toctree:: + :maxdepth: 1 + :caption: APIドキュメント + + modindex + search + +.. automodule:: adf_core_python + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/install/environment/environment.md b/docs/source/install/environment/environment.md new file mode 100644 index 00000000..e9cf04b7 --- /dev/null +++ b/docs/source/install/environment/environment.md @@ -0,0 +1,56 @@ +# 環境構築 + +adf-core-pythonをインストールするには以下の必要条件が必要です。 +既にお使いのPCにインストールされている場合は再度インストールする必要はありません。 + +## 必要条件 + +- Git +- Python 3.12 以上 +- OpenJDK 17 + +各OSでのインストール方法は以下のページをそれぞれ参照してください + +[Windowsでの必要条件のインストール方法](./windows/install.md) + +[MacOSでの必要条件のインストール方法](./mac/install.md) + +[Linuxでの必要条件のインストール方法](./linux/install.md) + +## シミュレーションサーバーのインストール + +次にRoboCup Rescue Simulationのシミュレーションサーバーをインストールします。 + +```{note} +WORKING_DIR は任意のディレクトリを作成、指定してください。 +``` + +```bash +mkdir WORKING_DIR +cd WORKING_DIR +git clone https://github.com/roborescue/rcrs-server.git +cd rcrs-server +./gradlew completeBuild +``` + +ビルドした際に以下のようなメッセージが表示されたら成功です。 + +```bash +BUILD SUCCESSFUL in ... +``` + +## シミュレーションサーバーの動作確認 + +```bash +cd scripts +./start-comprun.sh -m ../maps/test/map -c ../maps/test/config +``` + +![シミュレーションサーバーの起動](../../images/launch_server.png) + +上記のように何個かのウィンドウが表示されたら成功です。 +コマンドラインで `Ctrl + C` (MacOSの場合は `Command + C` ) を押すとシミュレーションサーバーが終了します。 + +```{warning} +シミュレーションサーバーを停止させたあとは、プロセスが残ってしまう場合があるので`./kill.sh` を実行してください。 +``` diff --git a/docs/source/install/environment/linux/install.md b/docs/source/install/environment/linux/install.md new file mode 100644 index 00000000..e431db1e --- /dev/null +++ b/docs/source/install/environment/linux/install.md @@ -0,0 +1,140 @@ +# Linuxでの環境構築 + +## 1. Gitのインストール + +1. Terminalを起動し、以下のコマンドを実行します。 + ```bash + git --version + ``` + +2. もし、`command not found`などのエラーが出た場合、OS標準のパッケージマネージャーを使用してインストールします。 + - DebianベースのOSの場合(Ubuntuなど) + ```bash + sudo apt update + sudo apt upgrade -y + sudo apt install git + ``` + - Red HatベースのOSの場合(Fedoraなど) + ```bash + sudo yum install git + ``` + + ```bash + sudo dnf install git + ``` + +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + git --version + ``` + +## 2. Pythonのインストール + +1. Terminalを起動し、以下のコマンドを実行します。また、バージョンが3.12以上になっていることを確認します。 + ```bash + python --version + ``` + +2. もし、`command not found`などのエラーが出た場合やバージョンが低い場合、Pythonのバージョン管理ツールであるpyenvを使用してインストールします + + ```{warning} + インストール方法の内容が最新ではない場合があるため、[https://github.com/pyenv/pyenv](https://github.com/pyenv/pyenv)を参照してください。 + ``` + + 1. 以下のコマンドを実行します。 + ```bash + curl https://pyenv.run | bash + ``` + + 2. 次に以下のコマンドを実行して、使用しているShellを確認します。 + ```bash + echo $SHELL + ``` + + 3. 表示されたShellに従ってコマンドを実行してください。 + + `bash`が表示された方は以下のコマンドを実行してください + ```bash + echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc + echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc + echo 'eval "$(pyenv init -)"' >> ~/.bashrc + + echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile + echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.profile + + echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile + echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile + echo 'eval "$(pyenv init -)"' >> ~/.bash_profile + ``` + + `zsh`が表示された方は以下のコマンドを実行してください + ```bash + echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc + echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc + echo 'eval "$(pyenv init -)"' >> ~/.zshrc + ``` + + `fish`が表示された方は以下のコマンドを実行してください + ```bash + set -Ux PYENV_ROOT $HOME/.pyenv + fish_add_path $PYENV_ROOT/bin + pyenv init - | source + ``` + + 4. 必要パッケージのインストール + - DebianベースのOSの場合(Ubuntuなど) + ```bash + sudo apt update + sudo apt upgrade -y + sudo apt install make libssl-dev build-essential zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev + ``` + - Red HatベースのOSの場合(Fedoraなど) + ```bash + sudo yum install gcc bzip2 bzip2-devel openssl openssl-devel readline readline-devel sqlite-devel tk-devel + ``` + + ```bash + sudo dnf install gcc bzip2 bzip2-devel openssl openssl-devel readline readline-devel sqlite-devel tk-devel + ``` + + 5. python3.12のインストール + ```bash + exec "$SHELL" + pyenv install 3.12 + pyenv global 3.12 + ``` + + + +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + python --version + ``` + +## 3. OpenJDKのインストール + +1. Terminalを起動し、以下のコマンドを実行します。また、バージョンが17になっていることを確認します。 + ```bash + java --version + ``` + +2. もし、`command not found`などのエラーが出た場合やバージョンが異なる場合、OS標準のパッケージマネージャーを使用してインストールします + - DebianベースのOSの場合(Ubuntuなど) + ```bash + sudo apt update + sudo apt upgrade -y + sudo apt install openjdk-17-jdk + ``` + - Red HatベースのOSの場合(Fedoraなど) + ```bash + sudo yum install java-17-openjdk-devel + ``` + + ```bash + sudo dnf install java-17-openjdk-devel + ``` + +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + java --version + ``` diff --git a/docs/source/install/environment/mac/install.md b/docs/source/install/environment/mac/install.md new file mode 100644 index 00000000..179406d8 --- /dev/null +++ b/docs/source/install/environment/mac/install.md @@ -0,0 +1,61 @@ +# Macでの環境構築 +## 1. Homebrewのインストール +1. Terminalを起動し、以下のコマンドを実行します。 + ```bash + brew -v + ``` + +2. もし、`command not found`などのエラーが出た場合、以下のコマンドを実行します。 + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + ``` + +3. もう一度、以下のコマンドを実行してバージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + brew -v + ``` + +## 2. Gitのインストール + +1. Terminalを起動し、以下のコマンドを実行します。 + ```bash + git --version + ``` +2. もし、`command not found`などのエラーが出た場合、以下のコマンドを実行します。 + ```bash + brew install git + ``` +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + git --version + ``` + +## 3. Pythonのインストール + +1. Terminalを起動し、以下のコマンドを実行します。また、バージョンが3.12以上になっていることを確認します。 + ```bash + python --version + ``` +2. もし、`command not found`などのエラーが出た場合やバージョンが低い場合、以下のコマンドを実行します。 + ```bash + brew install python + ``` +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + python --version + ``` + +## 3. OpenJDKのインストール + +1. Terminalを起動し、以下のコマンドを実行します。また、バージョンが17になっていることを確認します。 + ```bash + java --version + ``` +2. もし、`command not found`などのエラーが出た場合やバージョンが異なる場合、以下のコマンドを実行します。 + ```bash + brew install openjdk@17 + ``` +3. 以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください) + ```bash + java --version + ``` diff --git a/docs/source/install/environment/windows/install.md b/docs/source/install/environment/windows/install.md new file mode 100644 index 00000000..f2f8bd01 --- /dev/null +++ b/docs/source/install/environment/windows/install.md @@ -0,0 +1,46 @@ +# Windowsでの環境構築 + +## 1. Gitのインストール + +1. [Git for Windows](https://gitforwindows.org/)の公式サイトにアクセスします。 +2. トップページの"Download"をクリックします +3. ダウンロードが完了した後、インストーラーを実行します。 +4. 全て"Next"をクリックします。 +5. インストールが完了するまで待ちます。 +6. インストールが完了したら"Finish"をクリックします。 +7. 検索バーに"Git Bash"と入力し、Git Bashを実行します。 +8. 画面が表示されていたらインストール成功です。 + +## 2. Pythonのインストール + +1. [Python](https://www.python.org/)の公式サイトにアクセスします。 +2. トップページの"Download Python ~"をクリックします +3. ダウンロードが完了した後、インストーラーを実行します。 +4. "Add python.exe to PATH"にチェックが入っていることを確認した後、"Install Now"をクリックします。 +5. インストールが完了するまで待ちます。 +6. インストールが完了したら"Close"をクリックします。 +7. Git Bashを開き、`python --version`と入力し、`Python [バージョン]`が表示されたら成功です。(もし表示されない場合はGit Bashを開き直してください) + +## 3. OpenJDKのインストール + +1. [OpenJDK](https://jdk.java.net/archive/)のダウンロードページにアクセスします。 +2. 17.0.2のWindowsの横にある"zip"をクリックします。 +3. ダウンロードしたzipを展開(解凍)します。 +4. 展開(解凍)すると"jdk-17.0.2"のようなフォルダができるのを確認します。 +5. このフォルダ"jdk-17.0.2"を`C:¥`の直下に移動させます。 +6. Windowsでコマンドプロンプトを管理者として実行します。 +7. 開いたら以下のコマンドを実行します。 + ``` + powershell -command "[System.Environment]::SetEnvironmentVariable(\"JAVA_HOME\", \"c:\jdk-17.0.2\", \"Machine\")" + ``` +8. 次に以下のコマンドを実行します。 + ``` + powershell -command "$oldpath = [System.Environment]::GetEnvironmentVariable(\"Path\", \"Machine\"); $oldpath += \";c:\jdk-17.0.2\bin\"; [System.Environment]::SetEnvironmentVariable(\"Path\", $oldpath, \"Machine\")" + ``` +9. Git Bashを開き、`java -version`と入力します。 + 以下のような文字が表示されたらインストール成功です。 + ``` + openjdk version "17.0.2" 2022-01-18 + OpenJDK Runtime Environment (build 17.0.2+8-86) + OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing) + ``` \ No newline at end of file diff --git a/docs/source/install/install/install.md b/docs/source/install/install/install.md new file mode 100644 index 00000000..032e22cc --- /dev/null +++ b/docs/source/install/install/install.md @@ -0,0 +1,28 @@ +# インストール + +このセクションでは、パッケージのインストール方法について説明します。 + +## 前提条件 + +インストールする前に、以下の前提条件を確認してください: + +- Python 3.12 以上 +- pip + +## パッケージのインストール + +パッケージをインストールするには、次のコマンドを実行します: + +```bash +pip install git+https://github.com/adf-python/adf-core-python.git +``` + +## インストールの確認 + +インストールを確認するには、次のコマンドを実行します: + +```bash +pip show adf_core_python +``` + +パッケージが正しくインストールされている場合、パッケージのバージョン番号などが表示されます。 diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.cli.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.cli.po new file mode 100644 index 00000000..b20a78ca --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.cli.po @@ -0,0 +1,38 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.cli.rst:2 +msgid "adf\\_core\\_python.cli package" +msgstr "" + +#: ../../source/adf_core_python.cli.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.cli.rst:8 +msgid "adf\\_core\\_python.cli.cli module" +msgstr "" + +#: ../../source/adf_core_python.cli.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.ambulance.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.ambulance.po new file mode 100644 index 00000000..fe4db961 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.ambulance.po @@ -0,0 +1,52 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:2 +msgid "adf\\_core\\_python.core.agent.action.ambulance package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:8 +msgid "adf\\_core\\_python.core.agent.action.ambulance.action\\_load module" +msgstr "" + +#: adf_core_python.core.agent.action.ambulance.action_load.ActionLoad:1 +#: adf_core_python.core.agent.action.ambulance.action_rescue.ActionRescue:1 +#: adf_core_python.core.agent.action.ambulance.action_unload.ActionUnload:1 of +msgid "Bases: :py:class:`~adf_core_python.core.agent.action.action.Action`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:16 +msgid "adf\\_core\\_python.core.agent.action.ambulance.action\\_rescue module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:24 +msgid "adf\\_core\\_python.core.agent.action.ambulance.action\\_unload module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.ambulance.rst:32 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.common.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.common.po new file mode 100644 index 00000000..75bd5e4d --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.common.po @@ -0,0 +1,47 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.action.common.rst:2 +msgid "adf\\_core\\_python.core.agent.action.common package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.common.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.common.rst:8 +msgid "adf\\_core\\_python.core.agent.action.common.action\\_move module" +msgstr "" + +#: adf_core_python.core.agent.action.common.action_move.ActionMove:1 +#: adf_core_python.core.agent.action.common.action_rest.ActionRest:1 of +msgid "Bases: :py:class:`~adf_core_python.core.agent.action.action.Action`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.common.rst:16 +msgid "adf\\_core\\_python.core.agent.action.common.action\\_rest module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.common.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.fire.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.fire.po new file mode 100644 index 00000000..bb9922cb --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.fire.po @@ -0,0 +1,52 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:2 +msgid "adf\\_core\\_python.core.agent.action.fire package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:8 +msgid "adf\\_core\\_python.core.agent.action.fire.action\\_extinguish module" +msgstr "" + +#: adf_core_python.core.agent.action.fire.action_extinguish.ActionExtinguish:1 +#: adf_core_python.core.agent.action.fire.action_refill.ActionRefill:1 +#: adf_core_python.core.agent.action.fire.action_rescue.ActionRescue:1 of +msgid "Bases: :py:class:`~adf_core_python.core.agent.action.action.Action`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:16 +msgid "adf\\_core\\_python.core.agent.action.fire.action\\_refill module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:24 +msgid "adf\\_core\\_python.core.agent.action.fire.action\\_rescue module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.fire.rst:32 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.po new file mode 100644 index 00000000..b6471d88 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.action.rst:2 +msgid "adf\\_core\\_python.core.agent.action package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.rst:16 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.rst:19 +msgid "adf\\_core\\_python.core.agent.action.action module" +msgstr "" + +#: adf_core_python.core.agent.action.action.Action:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.rst:27 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.police.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.police.po new file mode 100644 index 00000000..c1e631fb --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.action.police.po @@ -0,0 +1,48 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.action.police.rst:2 +msgid "adf\\_core\\_python.core.agent.action.police package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.police.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.police.rst:8 +msgid "adf\\_core\\_python.core.agent.action.police.action\\_clear module" +msgstr "" + +#: adf_core_python.core.agent.action.police.action_clear.ActionClear:1 +#: adf_core_python.core.agent.action.police.action_clear_area.ActionClearArea:1 +#: of +msgid "Bases: :py:class:`~adf_core_python.core.agent.action.action.Action`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.police.rst:16 +msgid "adf\\_core\\_python.core.agent.action.police.action\\_clear\\_area module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.action.police.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.po new file mode 100644 index 00000000..93f8b721 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.po @@ -0,0 +1,251 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.rst:2 +msgid "adf\\_core\\_python.core.agent.communication package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst:13 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst:16 +msgid "adf\\_core\\_python.core.agent.communication.message\\_manager module" +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.add_heard_agent_help_message_count:1 +#: of +msgid "Add the heard agent help message count." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.add_message:1 +#: of +msgid "Add the message." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.add_message:3 +#: of +msgid "The message." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.add_received_message:1 +#: of +msgid "Add the received message." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.add_received_message:3 +#: of +msgid "The received message." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.coordinate_message:1 +#: of +msgid "Coordinate the message." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.coordinate_message:3 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.subscribe:3 +#: of +msgid "The agent info." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.coordinate_message:5 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.subscribe:5 +#: of +msgid "The world info." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.coordinate_message:7 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.subscribe:7 +#: of +msgid "The scenario info." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_channel_send_message_list:1 +#: of +msgid "Get the channel send message list." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_channel_send_message_list:3 +#: of +msgid "The channel send message list." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst +msgid "Return type" +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_channel_subscriber:1 +#: of +msgid "Get the channel subscriber." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_channel_subscriber:3 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_channel_subscriber:3 +#: of +msgid "The channel subscriber." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_heard_agent_help_message_count:1 +#: of +msgid "Get the heard agent help message count." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_heard_agent_help_message_count:3 +#: of +msgid "The heard agent help message count." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_is_subscribed:1 +#: of +msgid "Get the flag to indicate if the agent is subscribed to the channel." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_is_subscribed:3 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_is_subscribed:3 +#: of +msgid "The flag to indicate if the agent is subscribed to the channel." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class:1 +#: of +msgid "Get the message class." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class:3 +#: of +msgid "The index." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class:6 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class_index:3 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.register_message_class:3 +#: of +msgid "The message class." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class_index:1 +#: of +msgid "Get the message class index." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_message_class_index:6 +#: of +msgid "The message class index." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_received_message_list:1 +#: of +msgid "Get the received message list." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_received_message_list:3 +#: of +msgid "The received message list." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_send_message_list:1 +#: of +msgid "Get the send message list." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_send_message_list:3 +#: of +msgid "The send message list." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_subscribed_channels:1 +#: of +msgid "Get the subscribed channels." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.get_subscribed_channels:3 +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_subscribed_channels:3 +#: of +msgid "The subscribed channels." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.refresh:1 +#: of +msgid "Refresh the message manager." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.register_message_class:1 +#: of +msgid "Register the message class." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_channel_subscriber:1 +#: of +msgid "Set the channel subscriber." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_is_subscribed:1 +#: of +msgid "Set the flag to indicate if the agent is subscribed to the channel." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_message_coordinator:1 +#: of +msgid "Set the message coordinator." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_message_coordinator:3 +#: of +msgid "The message coordinator." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.set_subscribed_channels:1 +#: of +msgid "Set the subscribed channels." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.subscribe:1 +#: of +msgid "Subscribe to the channel." +msgstr "" + +#: adf_core_python.core.agent.communication.message_manager.MessageManager.subscribe:11 +#: of +msgid "If the ChannelSubscriber is not set." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.centralized.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.centralized.po new file mode 100644 index 00000000..8796bc33 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.centralized.po @@ -0,0 +1,77 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:2 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized " +"package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:8 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized.command\\_ambulance" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance.CommandAmbulance:1 +#: adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire.CommandFire:1 +#: adf_core_python.core.agent.communication.standard.bundle.centralized.command_police.CommandPolice:1 +#: adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout.CommandScout:1 +#: adf_core_python.core.agent.communication.standard.bundle.centralized.message_report.MessageReport:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.agent.communication.standard.bundle.standard_message.StandardMessage`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:16 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized.command\\_fire" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:24 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized.command\\_police" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:32 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized.command\\_scout" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:40 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.centralized.message\\_report" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.centralized.rst:48 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.information.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.information.po new file mode 100644 index 00000000..46d33d0d --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.information.po @@ -0,0 +1,84 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:2 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information " +"package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:8 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_ambulance\\_team" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team.MessageAmbulanceTeam:1 +#: adf_core_python.core.agent.communication.standard.bundle.information.message_building.MessageBuilding:1 +#: adf_core_python.core.agent.communication.standard.bundle.information.message_civilian.MessageCivilian:1 +#: adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade.MessageFireBrigade:1 +#: adf_core_python.core.agent.communication.standard.bundle.information.message_police_force.MessagePoliceForce:1 +#: adf_core_python.core.agent.communication.standard.bundle.information.message_road.MessageRoad:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.agent.communication.standard.bundle.standard_message.StandardMessage`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:16 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_building" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:24 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_civilian" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:32 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_fire\\_brigade" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:40 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_police\\_force" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:48 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.information.message\\_road" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.information.rst:56 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.po new file mode 100644 index 00000000..29b826dc --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.bundle.po @@ -0,0 +1,67 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:2 +msgid "adf\\_core\\_python.core.agent.communication.standard.bundle package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:14 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:17 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.standard\\_message" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.bundle.standard_message.StandardMessage:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.communication.communication_message.CommunicationMessage`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:25 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.bundle.standard\\_message\\_priority" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.bundle.standard_message_priority.StandardMessagePriority:1 +#: of +msgid "Bases: :py:class:`~enum.Enum`" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.bundle.standard_message_priority.StandardMessagePriority:1 +#: of +msgid "Standard message priorities." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.bundle.rst:33 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.po new file mode 100644 index 00000000..0e0d1293 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.po @@ -0,0 +1,51 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.standard.rst:2 +msgid "adf\\_core\\_python.core.agent.communication.standard package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.rst:14 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.rst:17 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.standard\\_communication\\_module" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.standard_communication_module.StandardCommunicationModule:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.communication.communication_module.CommunicationModule`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.rst:25 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.utility.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.utility.po new file mode 100644 index 00000000..69e19d9b --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.communication.standard.utility.po @@ -0,0 +1,133 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst:2 +msgid "adf\\_core\\_python.core.agent.communication.standard.utility package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst:8 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.utility.apply\\_to\\_world\\_info" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.apply_to_world_info.apply_to_world_info:1 +#: of +msgid "Apply to world info." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.apply_to_world_info.apply_to_world_info:3 +#: of +msgid "The world info to apply to." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.apply_to_world_info.apply_to_world_info:5 +#: of +msgid "The standard message to apply to world info." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst:16 +msgid "" +"adf\\_core\\_python.core.agent.communication.standard.utility.bitarray\\_with\\_exits\\_flag" +" module" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.read_with_exist_flag:1 +#: of +msgid "" +"Read value from bit_array with an exist flag. If the first bit is " +"IS_NOT_EXIST_FLAG, return None. If the first bit is IS_EXIST_FLAG, read " +"and return value from bit_array." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.read_with_exist_flag:5 +#: of +msgid "The bitarray to read from." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.read_with_exist_flag:7 +#: of +msgid "The number of bits to read to get value." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.read_with_exist_flag:10 +#: of +msgid "The value read from bit_array." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst +msgid "Return type" +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst +msgid "Raises" +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.read_with_exist_flag:13 +#: of +msgid "If the first bit is not IS_EXIST_FLAG or IS_NOT_EXIST_FLAG." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.write_with_exist_flag:1 +#: of +msgid "" +"Write value to bit_array with an exist flag. If value is None, write " +"IS_NOT_EXIST_FLAG to bit_array. If value is not None, write IS_EXIST_FLAG" +" to bit_array and then write value to bit_array." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.write_with_exist_flag:5 +#: of +msgid "The bitarray to write to." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.write_with_exist_flag:7 +#: of +msgid "The value to write." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.write_with_exist_flag:9 +#: of +msgid "The number of bits to use to write value." +msgstr "" + +#: adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag.write_with_exist_flag:12 +#: of +msgid "If value is too large to fit into bit_size bits." +msgstr "" + +#: ../../source/adf_core_python.core.agent.communication.standard.utility.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.config.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.config.po new file mode 100644 index 00000000..e647ed2b --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.config.po @@ -0,0 +1,42 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.config.rst:2 +msgid "adf\\_core\\_python.core.agent.config package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.config.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.config.rst:8 +msgid "adf\\_core\\_python.core.agent.config.module\\_config module" +msgstr "" + +#: adf_core_python.core.agent.config.module_config.ModuleConfig:1 of +msgid "Bases: :py:class:`~rcrs_core.config.config.Config`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.config.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.develop.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.develop.po new file mode 100644 index 00000000..70a33eee --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.develop.po @@ -0,0 +1,78 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.develop.rst:2 +msgid "adf\\_core\\_python.core.agent.develop package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst:8 +msgid "adf\\_core\\_python.core.agent.develop.develop\\_data module" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData.get_value:1 of +msgid "" +"Get value from develop data If develop mode is disabled, return default " +"value" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData.get_value:4 of +msgid "Key" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData.get_value:7 of +msgid "Value" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst +msgid "Return type" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData.is_develop_mode:1 +#: of +msgid "Check if develop mode is enabled" +msgstr "" + +#: adf_core_python.core.agent.develop.develop_data.DevelopData.is_develop_mode:3 +#: of +msgid "True if develop mode is enabled" +msgstr "" + +#: ../../source/adf_core_python.core.agent.develop.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.info.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.info.po new file mode 100644 index 00000000..e50dbf13 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.info.po @@ -0,0 +1,317 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.info.rst:2 +msgid "adf\\_core\\_python.core.agent.info package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst:8 +msgid "adf\\_core\\_python.core.agent.info.agent\\_info module" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo:1 +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo:1 +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfoKeys:1 +#: adf_core_python.core.agent.info.world_info.WorldInfo:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_change_set:1 +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_change_set:1 of +msgid "Get the change set" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_change_set:3 +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_change_set:3 +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_change_set:3 +#: adf_core_python.core.agent.info.world_info.WorldInfo.set_change_set:3 of +msgid "Change set" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst +msgid "Return type" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_entity_id:1 of +msgid "Get the entity ID of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_entity_id:3 of +msgid "Entity ID of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_executed_action:1 +#: of +msgid "Get the executed action at the given time" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_executed_action:3 +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_executed_action:3 +#: of +msgid "Time" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_heard_commands:1 of +msgid "Get the heard commands" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_heard_commands:3 +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_heard_commands:3 of +msgid "Heard commands" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_myself:1 of +msgid "Get the entity of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_myself:3 of +msgid "Entity of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_position_entity_id:1 +#: of +msgid "Get the position entity ID of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_position_entity_id:3 +#: of +msgid "Position entity ID of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_think_time:1 of +msgid "Get the time taken for thinking" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_think_time:3 of +msgid "Time taken for thinking" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_time:1 of +msgid "Get the current time of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.get_time:3 +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_time:3 of +msgid "Current time" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.record_think_start_time:1 +#: of +msgid "Record the start time of thinking" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_change_set:1 +#: adf_core_python.core.agent.info.world_info.WorldInfo.set_change_set:1 of +msgid "Set the change set" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_executed_action:1 +#: of +msgid "Set the executed action at the given time" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_executed_action:5 +#: of +msgid "Executed action" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_heard_commands:1 of +msgid "Set the heard commands" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.set_time:1 of +msgid "Set the current time of the agent" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.some_one_on_board:1 of +msgid "Get the human if someone is on board" +msgstr "" + +#: adf_core_python.core.agent.info.agent_info.AgentInfo.some_one_on_board:3 of +msgid "Human if someone is on board, None otherwise" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst:16 +msgid "adf\\_core\\_python.core.agent.info.scenario\\_info module" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.Mode:1 of +msgid "Bases: :py:class:`~enum.IntEnum`" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_config:1 of +msgid "Get the configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_config:3 +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.set_config:3 of +msgid "Configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_mode:1 of +msgid "Get the mode of the scenario" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_mode:3 of +msgid "Mode of the scenario" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_value:1 of +msgid "Get the value of the configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_value:3 of +msgid "Key of the configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_value:5 of +msgid "Default value of the configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.get_value:8 of +msgid "Value of the configuration" +msgstr "" + +#: adf_core_python.core.agent.info.scenario_info.ScenarioInfo.set_config:1 of +msgid "Set the configuration" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst:24 +msgid "adf\\_core\\_python.core.agent.info.world\\_info module" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.add_entity:1 of +msgid "Add the entity" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.add_entity:3 +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity:6 of +msgid "Entity" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_blockades:1 of +msgid "Get the blockades in the area" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_blockades:3 of +msgid "Blockade" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_distance:1 of +msgid "Get the distance between two entities" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_distance:3 of +msgid "Entity ID 1" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_distance:5 of +msgid "Entity ID 2" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_distance:8 of +msgid "Distance" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst +msgid "Raises" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_distance:11 of +msgid "If one or both entities are invalid or the location is invalid" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entities_of_types:1 +#: of +msgid "Get the entities of the specified types" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entities_of_types:3 +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_ids_of_types:3 +#: of +msgid "List of entity types" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entities_of_types:6 +#: of +msgid "Entities" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity:1 of +msgid "Get the entity" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity:3 +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_position:3 +#: of +msgid "Entity ID" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_ids_of_types:1 +#: of +msgid "Get the entity IDs of the specified types" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_ids_of_types:6 +#: of +msgid "Entity IDs" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_position:1 +#: of +msgid "Get the entity position" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_position:6 +#: of +msgid "Entity position" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_entity_position:9 +#: of +msgid "If the entity is invalid" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_world_model:1 of +msgid "Get the world model" +msgstr "" + +#: adf_core_python.core.agent.info.world_info.WorldInfo.get_world_model:3 of +msgid "World model" +msgstr "" + +#: ../../source/adf_core_python.core.agent.info.rst:32 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.module.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.module.po new file mode 100644 index 00000000..b98e2fc0 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.module.po @@ -0,0 +1,42 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.module.rst:2 +msgid "adf\\_core\\_python.core.agent.module package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.module.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.module.rst:8 +msgid "adf\\_core\\_python.core.agent.module.module\\_manager module" +msgstr "" + +#: adf_core_python.core.agent.module.module_manager.ModuleManager:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.module.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.platoon.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.platoon.po new file mode 100644 index 00000000..fee57f72 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.platoon.po @@ -0,0 +1,60 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.platoon.rst:2 +msgid "adf\\_core\\_python.core.agent.platoon package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:8 +msgid "adf\\_core\\_python.core.agent.platoon.platoon module" +msgstr "" + +#: adf_core_python.core.agent.platoon.platoon.Platoon:1 of +msgid "Bases: :py:class:`~adf_core_python.core.agent.agent.Agent`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:16 +msgid "adf\\_core\\_python.core.agent.platoon.platoon\\_ambulance module" +msgstr "" + +#: adf_core_python.core.agent.platoon.platoon_ambulance.PlatoonAmbulance:1 +#: adf_core_python.core.agent.platoon.platoon_fire.PlatoonFire:1 +#: adf_core_python.core.agent.platoon.platoon_police.PlatoonPolice:1 of +msgid "Bases: :py:class:`~adf_core_python.core.agent.platoon.platoon.Platoon`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:24 +msgid "adf\\_core\\_python.core.agent.platoon.platoon\\_fire module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:32 +msgid "adf\\_core\\_python.core.agent.platoon.platoon\\_police module" +msgstr "" + +#: ../../source/adf_core_python.core.agent.platoon.rst:40 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.po new file mode 100644 index 00000000..d9d8a76e --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.rst:2 +msgid "adf\\_core\\_python.core.agent package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.agent.rst:20 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.rst:23 +msgid "adf\\_core\\_python.core.agent.agent module" +msgstr "" + +#: adf_core_python.core.agent.agent.Agent:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.core.agent.rst:31 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.precompute.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.precompute.po new file mode 100644 index 00000000..67620c43 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.agent.precompute.po @@ -0,0 +1,93 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.agent.precompute.rst:2 +msgid "adf\\_core\\_python.core.agent.precompute package" +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst:8 +msgid "adf\\_core\\_python.core.agent.precompute.precompute\\_data module" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.is_available:1 +#: of +msgid "Check if the precompute data is available." +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.is_available:3 +#: of +msgid "True if the precompute data is available, False otherwise." +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst +msgid "Return type" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.read_json_data:1 +#: of +msgid "Read the precompute data from the file." +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.read_json_data:3 +#: of +msgid "The precompute data." +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst +msgid "Raises" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.remove_precompute_data:1 +#: of +msgid "Remove the precompute data file." +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.write_json_data:1 +#: of +msgid "Write the precompute data to the file." +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.agent.precompute.precompute_data.PrecomputeData.write_json_data:3 +#: of +msgid "The data to write." +msgstr "" + +#: ../../source/adf_core_python.core.agent.precompute.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.action.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.action.po new file mode 100644 index 00000000..6d326d0b --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.action.po @@ -0,0 +1,42 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.action.rst:2 +msgid "adf\\_core\\_python.core.component.action package" +msgstr "" + +#: ../../source/adf_core_python.core.component.action.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.action.rst:8 +msgid "adf\\_core\\_python.core.component.action.extend\\_action module" +msgstr "" + +#: adf_core_python.core.component.action.extend_action.ExtendAction:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.component.action.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.communication.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.communication.po new file mode 100644 index 00000000..1e125a3d --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.communication.po @@ -0,0 +1,103 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.communication.rst:2 +msgid "adf\\_core\\_python.core.component.communication package" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:8 +msgid "" +"adf\\_core\\_python.core.component.communication.channel\\_subscriber " +"module" +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber:1 +#: adf_core_python.core.component.communication.communication_message.CommunicationMessage:1 +#: adf_core_python.core.component.communication.communication_module.CommunicationModule:1 +#: adf_core_python.core.component.communication.message_coordinator.MessageCoordinator:1 +#: of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber.subscribe:1 +#: of +msgid "Subscribe to the channel." +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber.subscribe:3 +#: of +msgid "The agent info." +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber.subscribe:5 +#: of +msgid "The world info." +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber.subscribe:7 +#: of +msgid "The scenario info." +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber.subscribe:10 +#: of +msgid "The list of subscribed channels." +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst +msgid "Return type" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:16 +msgid "" +"adf\\_core\\_python.core.component.communication.communication\\_message " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:24 +msgid "" +"adf\\_core\\_python.core.component.communication.communication\\_module " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:32 +msgid "" +"adf\\_core\\_python.core.component.communication.message\\_coordinator " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.component.communication.rst:40 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.algorithm.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.algorithm.po new file mode 100644 index 00000000..33fd90eb --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.algorithm.po @@ -0,0 +1,50 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.module.algorithm.rst:2 +msgid "adf\\_core\\_python.core.component.module.algorithm package" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.algorithm.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.algorithm.rst:8 +msgid "adf\\_core\\_python.core.component.module.algorithm.clustering module" +msgstr "" + +#: adf_core_python.core.component.module.algorithm.clustering.Clustering:1 +#: adf_core_python.core.component.module.algorithm.path_planning.PathPlanning:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.abstract_module.AbstractModule`" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.algorithm.rst:16 +msgid "adf\\_core\\_python.core.component.module.algorithm.path\\_planning module" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.algorithm.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.complex.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.complex.po new file mode 100644 index 00000000..65e3113d --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.complex.po @@ -0,0 +1,122 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.module.complex.rst:2 +msgid "adf\\_core\\_python.core.component.module.complex package" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:8 +msgid "" +"adf\\_core\\_python.core.component.module.complex.ambulance\\_target\\_allocator" +" module" +msgstr "" + +#: adf_core_python.core.component.module.complex.ambulance_target_allocator.AmbulanceTargetAllocator:1 +#: adf_core_python.core.component.module.complex.fire_target_allocator.FireTargetAllocator:1 +#: adf_core_python.core.component.module.complex.police_target_allocator.PoliceTargetAllocator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.target_allocator.TargetAllocator`" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:16 +msgid "" +"adf\\_core\\_python.core.component.module.complex.fire\\_target\\_allocator" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:24 +msgid "adf\\_core\\_python.core.component.module.complex.human\\_detector module" +msgstr "" + +#: adf_core_python.core.component.module.complex.human_detector.HumanDetector:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.target_detector.TargetDetector`\\" +" [:py:class:`~rcrs_core.entities.human.Human`]" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:32 +msgid "" +"adf\\_core\\_python.core.component.module.complex.police\\_target\\_allocator" +" module" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:40 +msgid "adf\\_core\\_python.core.component.module.complex.road\\_detector module" +msgstr "" + +#: adf_core_python.core.component.module.complex.road_detector.RoadDetector:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.target_detector.TargetDetector`\\" +" [:py:class:`~rcrs_core.entities.road.Road`]" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:48 +msgid "adf\\_core\\_python.core.component.module.complex.search module" +msgstr "" + +#: adf_core_python.core.component.module.complex.search.Search:1 of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.target_detector.TargetDetector`\\" +" [:py:class:`~rcrs_core.entities.area.Area`]" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:56 +msgid "" +"adf\\_core\\_python.core.component.module.complex.target\\_allocator " +"module" +msgstr "" + +#: adf_core_python.core.component.module.complex.target_allocator.TargetAllocator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.abstract_module.AbstractModule`" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:64 +msgid "adf\\_core\\_python.core.component.module.complex.target\\_detector module" +msgstr "" + +#: adf_core_python.core.component.module.complex.target_detector.TargetDetector:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.abstract_module.AbstractModule`," +" :py:class:`~typing.Generic`\\ " +"[:py:obj:`~adf_core_python.core.component.module.complex.target_detector.T`]" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.complex.rst:72 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.po new file mode 100644 index 00000000..4a2d3ba3 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.module.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.module.rst:2 +msgid "adf\\_core\\_python.core.component.module package" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.rst:14 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.rst:17 +msgid "adf\\_core\\_python.core.component.module.abstract\\_module module" +msgstr "" + +#: adf_core_python.core.component.module.abstract_module.AbstractModule:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.component.module.rst:25 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.po new file mode 100644 index 00000000..448531cf --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.rst:2 +msgid "adf\\_core\\_python.core.component package" +msgstr "" + +#: ../../source/adf_core_python.core.component.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.component.rst:16 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.rst:19 +msgid "adf\\_core\\_python.core.component.abstract\\_loader module" +msgstr "" + +#: adf_core_python.core.component.abstract_loader.AbstractLoader:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.component.rst:27 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.tactics.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.tactics.po new file mode 100644 index 00000000..c32d3193 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.component.tactics.po @@ -0,0 +1,95 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.component.tactics.rst:2 +msgid "adf\\_core\\_python.core.component.tactics package" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:8 +msgid "adf\\_core\\_python.core.component.tactics.tactics\\_agent module" +msgstr "" + +#: adf_core_python.core.component.tactics.tactics_agent.TacticsAgent:1 +#: adf_core_python.core.component.tactics.tactics_center.TacticsCenter:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:16 +msgid "" +"adf\\_core\\_python.core.component.tactics.tactics\\_ambulance\\_center " +"module" +msgstr "" + +#: adf_core_python.core.component.tactics.tactics_ambulance_center.TacticsAmbulanceCenter:1 +#: adf_core_python.core.component.tactics.tactics_fire_station.TacticsFireStation:1 +#: adf_core_python.core.component.tactics.tactics_police_office.TacticsPoliceOffice:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_center.TacticsCenter`" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:24 +msgid "" +"adf\\_core\\_python.core.component.tactics.tactics\\_ambulance\\_team " +"module" +msgstr "" + +#: adf_core_python.core.component.tactics.tactics_ambulance_team.TacticsAmbulanceTeam:1 +#: adf_core_python.core.component.tactics.tactics_fire_brigade.TacticsFireBrigade:1 +#: adf_core_python.core.component.tactics.tactics_police_force.TacticsPoliceForce:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_agent.TacticsAgent`" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:32 +msgid "adf\\_core\\_python.core.component.tactics.tactics\\_center module" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:40 +msgid "adf\\_core\\_python.core.component.tactics.tactics\\_fire\\_brigade module" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:48 +msgid "adf\\_core\\_python.core.component.tactics.tactics\\_fire\\_station module" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:56 +msgid "adf\\_core\\_python.core.component.tactics.tactics\\_police\\_force module" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:64 +msgid "" +"adf\\_core\\_python.core.component.tactics.tactics\\_police\\_office " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.component.tactics.rst:72 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.config.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.config.po new file mode 100644 index 00000000..47ee2ded --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.config.po @@ -0,0 +1,42 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.config.rst:2 +msgid "adf\\_core\\_python.core.config package" +msgstr "" + +#: ../../source/adf_core_python.core.config.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.config.rst:8 +msgid "adf\\_core\\_python.core.config.config module" +msgstr "" + +#: adf_core_python.core.config.config.Config:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.core.config.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.connect.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.connect.po new file mode 100644 index 00000000..dc9c05d7 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.connect.po @@ -0,0 +1,134 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.launcher.connect.rst:2 +msgid "adf\\_core\\_python.core.launcher.connect package" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:8 +msgid "adf\\_core\\_python.core.launcher.connect.component\\_launcher module" +msgstr "" + +#: adf_core_python.core.launcher.connect.component_launcher.ComponentLauncher:1 +#: adf_core_python.core.launcher.connect.connection.Connection:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:16 +msgid "adf\\_core\\_python.core.launcher.connect.connection module" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.connect:1 of +msgid "Connect to the kernel" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst +msgid "Raises" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.connect:3 of +msgid "If the connection times out" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.connect:4 of +msgid "If there is an error connecting to the socket" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.parse_message_from_kernel:1 +#: of +msgid "Parse messages from the kernel" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.parse_message_from_kernel:3 +#: of +msgid "If there is an error reading from the socket" +msgstr "" + +#: adf_core_python.core.launcher.connect.connection.Connection.parse_message_from_kernel:4 +#: of +msgid "If there is an error in the agent calculation" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:24 +msgid "adf\\_core\\_python.core.launcher.connect.connector module" +msgstr "" + +#: adf_core_python.core.launcher.connect.connector.Connector:1 of +msgid "Bases: :py:class:`~abc.ABC`" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:32 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_ambulance\\_center " +"module" +msgstr "" + +#: adf_core_python.core.launcher.connect.connector_ambulance_center.ConnectorAmbulanceCenter:1 +#: adf_core_python.core.launcher.connect.connector_ambulance_team.ConnectorAmbulanceTeam:1 +#: adf_core_python.core.launcher.connect.connector_fire_brigade.ConnectorFireBrigade:1 +#: adf_core_python.core.launcher.connect.connector_fire_station.ConnectorFireStation:1 +#: adf_core_python.core.launcher.connect.connector_police_force.ConnectorPoliceForce:1 +#: adf_core_python.core.launcher.connect.connector_police_office.ConnectorPoliceOffice:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.launcher.connect.connector.Connector`" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:40 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_ambulance\\_team " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:48 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_fire\\_brigade " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:56 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_fire\\_station " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:64 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_police\\_force " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:72 +msgid "" +"adf\\_core\\_python.core.launcher.connect.connector\\_police\\_office " +"module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.connect.rst:80 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.po new file mode 100644 index 00000000..ae5e4512 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.launcher.po @@ -0,0 +1,51 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.launcher.rst:2 +msgid "adf\\_core\\_python.core.launcher package" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.rst:13 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.rst:16 +msgid "adf\\_core\\_python.core.launcher.agent\\_launcher module" +msgstr "" + +#: adf_core_python.core.launcher.agent_launcher.AgentLauncher:1 +#: adf_core_python.core.launcher.config_key.ConfigKey:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.rst:24 +msgid "adf\\_core\\_python.core.launcher.config\\_key module" +msgstr "" + +#: ../../source/adf_core_python.core.launcher.rst:32 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.logger.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.logger.po new file mode 100644 index 00000000..0890f2a4 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.logger.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.logger.rst:2 +msgid "adf\\_core\\_python.core.logger package" +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst:8 +msgid "adf\\_core\\_python.core.logger.logger module" +msgstr "" + +#: adf_core_python.core.logger.logger.get_agent_logger:1 of +msgid "" +"Get a logger with the given name and agent information. For agent " +"logging, use this function to get a logger." +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.core.logger.logger.get_agent_logger:4 +#: adf_core_python.core.logger.logger.get_logger:4 of +msgid "The name of the logger." +msgstr "" + +#: adf_core_python.core.logger.logger.get_agent_logger:6 of +msgid "The agent information." +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.core.logger.logger.get_agent_logger:9 of +msgid "The logger with the given name and agent information." +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst +msgid "Return type" +msgstr "" + +#: adf_core_python.core.logger.logger.get_logger:1 of +msgid "" +"Get a logger with the given name. For kernel logging, use this function " +"to get a logger." +msgstr "" + +#: adf_core_python.core.logger.logger.get_logger:7 of +msgid "The logger with the given name." +msgstr "" + +#: ../../source/adf_core_python.core.logger.rst:16 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.po new file mode 100644 index 00000000..5da18eb8 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.core.po @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.core.rst:2 +msgid "adf\\_core\\_python.core package" +msgstr "" + +#: ../../source/adf_core_python.core.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.core.rst:17 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.action.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.action.po new file mode 100644 index 00000000..f8504891 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.action.po @@ -0,0 +1,68 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.action.rst:2 +msgid "adf\\_core\\_python.implement.action package" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:8 +msgid "" +"adf\\_core\\_python.implement.action.default\\_extend\\_action\\_clear " +"module" +msgstr "" + +#: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear:1 +#: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove:1 +#: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue:1 +#: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.action.extend_action.ExtendAction`" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:16 +msgid "" +"adf\\_core\\_python.implement.action.default\\_extend\\_action\\_move " +"module" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:24 +msgid "" +"adf\\_core\\_python.implement.action.default\\_extend\\_action\\_rescue " +"module" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:32 +msgid "" +"adf\\_core\\_python.implement.action.default\\_extend\\_action\\_transport" +" module" +msgstr "" + +#: ../../source/adf_core_python.implement.action.rst:40 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.algorithm.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.algorithm.po new file mode 100644 index 00000000..5bab5300 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.algorithm.po @@ -0,0 +1,60 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.module.algorithm.rst:2 +msgid "adf\\_core\\_python.implement.module.algorithm package" +msgstr "" + +#: ../../source/adf_core_python.implement.module.algorithm.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.module.algorithm.rst:8 +msgid "" +"adf\\_core\\_python.implement.module.algorithm.a\\_star\\_path\\_planning" +" module" +msgstr "" + +#: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.algorithm.path_planning.PathPlanning`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.algorithm.rst:16 +msgid "" +"adf\\_core\\_python.implement.module.algorithm.k\\_means\\_clustering " +"module" +msgstr "" + +#: adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.algorithm.clustering.Clustering`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.algorithm.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.communication.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.communication.po new file mode 100644 index 00000000..a598623e --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.communication.po @@ -0,0 +1,97 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.module.communication.rst:2 +msgid "adf\\_core\\_python.implement.module.communication package" +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst:8 +msgid "" +"adf\\_core\\_python.implement.module.communication.default\\_channel\\_subscriber" +" module" +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.communication.channel_subscriber.ChannelSubscriber`" +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber.subscribe:1 +#: of +msgid "Subscribe to the channel." +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst +msgid "Parameters" +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber.subscribe:3 +#: of +msgid "The agent info." +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber.subscribe:5 +#: of +msgid "The world info." +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber.subscribe:7 +#: of +msgid "The scenario info." +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst +msgid "Returns" +msgstr "" + +#: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber.subscribe:10 +#: of +msgid "The list of subscribed channels." +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst +msgid "Return type" +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst:16 +msgid "" +"adf\\_core\\_python.implement.module.communication.default\\_message\\_coordinator" +" module" +msgstr "" + +#: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.communication.message_coordinator.MessageCoordinator`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.communication.rst:24 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.complex.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.complex.po new file mode 100644 index 00000000..1c260b94 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.complex.po @@ -0,0 +1,116 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.module.complex.rst:2 +msgid "adf\\_core\\_python.implement.module.complex package" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:8 +msgid "" +"adf\\_core\\_python.implement.module.complex.default\\_ambulance\\_target\\_allocator" +" module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.ambulance_target_allocator.AmbulanceTargetAllocator`" +msgstr "" + +#: adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator.AmbulanceTeamInfo:1 +#: adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator.FireBrigadeInfo:1 +#: adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator.PoliceForceInfo:1 +#: of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:16 +msgid "" +"adf\\_core\\_python.implement.module.complex.default\\_fire\\_target\\_allocator" +" module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.fire_target_allocator.FireTargetAllocator`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:24 +msgid "" +"adf\\_core\\_python.implement.module.complex.default\\_human\\_detector " +"module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.human_detector.HumanDetector`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:32 +msgid "" +"adf\\_core\\_python.implement.module.complex.default\\_police\\_target\\_allocator" +" module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.police_target_allocator.PoliceTargetAllocator`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:40 +msgid "" +"adf\\_core\\_python.implement.module.complex.default\\_road\\_detector " +"module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_road_detector.DefaultRoadDetector:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.road_detector.RoadDetector`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:48 +msgid "adf\\_core\\_python.implement.module.complex.default\\_search module" +msgstr "" + +#: adf_core_python.implement.module.complex.default_search.DefaultSearch:1 of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.module.complex.search.Search`" +msgstr "" + +#: ../../source/adf_core_python.implement.module.complex.rst:56 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.po new file mode 100644 index 00000000..8f4619a5 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.module.po @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.module.rst:2 +msgid "adf\\_core\\_python.implement.module package" +msgstr "" + +#: ../../source/adf_core_python.implement.module.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.implement.module.rst:15 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.po new file mode 100644 index 00000000..2d6cdd9f --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.po @@ -0,0 +1,48 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.rst:2 +msgid "adf\\_core\\_python.implement package" +msgstr "" + +#: ../../source/adf_core_python.implement.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.implement.rst:15 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.rst:18 +msgid "adf\\_core\\_python.implement.default\\_loader module" +msgstr "" + +#: adf_core_python.implement.default_loader.DefaultLoader:1 of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.abstract_loader.AbstractLoader`" +msgstr "" + +#: ../../source/adf_core_python.implement.rst:26 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.tactics.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.tactics.po new file mode 100644 index 00000000..27c82fc0 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.implement.tactics.po @@ -0,0 +1,112 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.implement.tactics.rst:2 +msgid "adf\\_core\\_python.implement.tactics package" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:5 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:8 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_ambulance\\_center" +" module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_ambulance_center.DefaultTacticsAmbulanceCenter:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_ambulance_center.TacticsAmbulanceCenter`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:16 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_ambulance\\_team" +" module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_ambulance_team.DefaultTacticsAmbulanceTeam:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_ambulance_team.TacticsAmbulanceTeam`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:24 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_fire\\_brigade " +"module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_fire_brigade.DefaultTacticsFireBrigade:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_fire_brigade.TacticsFireBrigade`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:32 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_fire\\_station " +"module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_fire_station.DefaultTacticsFireStation:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_fire_station.TacticsFireStation`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:40 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_police\\_force " +"module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_police_force.DefaultTacticsPoliceForce:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_police_force.TacticsPoliceForce`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:48 +msgid "" +"adf\\_core\\_python.implement.tactics.default\\_tactics\\_police\\_office" +" module" +msgstr "" + +#: adf_core_python.implement.tactics.default_tactics_police_office.DefaultTacticsPoliceOffice:1 +#: of +msgid "" +"Bases: " +":py:class:`~adf_core_python.core.component.tactics.tactics_police_office.TacticsPoliceOffice`" +msgstr "" + +#: ../../source/adf_core_python.implement.tactics.rst:56 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/adf_core_python.po b/docs/source/locale/en/LC_MESSAGES/adf_core_python.po new file mode 100644 index 00000000..537693c5 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/adf_core_python.po @@ -0,0 +1,46 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/adf_core_python.rst:2 +msgid "adf\\_core\\_python package" +msgstr "" + +#: ../../source/adf_core_python.rst:5 +msgid "Subpackages" +msgstr "" + +#: ../../source/adf_core_python.rst:15 +msgid "Submodules" +msgstr "" + +#: ../../source/adf_core_python.rst:18 +msgid "adf\\_core\\_python.launcher module" +msgstr "" + +#: adf_core_python.launcher.Launcher:1 of +msgid "Bases: :py:class:`object`" +msgstr "" + +#: ../../source/adf_core_python.rst:26 +msgid "Module contents" +msgstr "" + diff --git a/docs/source/locale/en/LC_MESSAGES/hands-on.po b/docs/source/locale/en/LC_MESSAGES/hands-on.po new file mode 100644 index 00000000..47429b6f --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/hands-on.po @@ -0,0 +1,284 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/hands-on/clustering.md:1 +msgid "クラスタリングモジュール" +msgstr "Clustering Module" + +#: ../../source/hands-on/clustering.md:3 +msgid "クラスタリングモジュールの目的" +msgstr "Purpose of the Clustering Module" + +#: ../../source/hands-on/clustering.md:5 +msgid "複数のエージェントを動かす場合は、それらのエージェントにどのように協調させるかが重要になります。RRSでは多くのチームが、エージェントに各々の担当地域を持たせ役割分担をおこなう協調を取り入れています(他の手段による協調も取り入れています)。担当地域を割り振るためには、地図上のオブジェクトをいくつかのグループに分ける必要があります。このようなグループ分けをしてそれらを管理する場合には、クラスタリングモジュールと呼ばれるモジュールを用います。" +msgstr "When operating multiple agents, it is crucial to determine how to coordinate them. In RRS, many teams adopt coordination methods that assign each agent a designated area of responsibility (as well as other coordination methods). To assign these areas, objects on the map need to be divided into several groups. A module called the clustering module is used to manage these groupings." + +#: ../../source/hands-on/clustering.md:7 +msgid "本資料では、多くの世界大会参加チームが使用しているアルゴリズムを用いたクラスタリングモジュールの実装をおこないます。" +msgstr "In this document, we will implement a clustering module using algorithms commonly employed by many teams participating in world championships." + +#: ../../source/hands-on/clustering.md:9 +msgid "開発するクラスタリングモジュールの概要" +msgstr "Overview of the Clustering Module to Be Developed" + +#: ../../source/hands-on/clustering.md:11 +msgid "本資料で開発するモジュールは下の画像のように、" +msgstr "The module developed in this document, as shown in the image below," + +#: ../../source/hands-on/clustering.md:13 +msgid "k-means++アルゴリズムによって地図上のオブジェクトをエージェント数分の区画に分けます。" +msgstr "Divides the objects on the map into sections equal to the number of agents using the k-means++ algorithm." + +#: ../../source/hands-on/clustering.md:14 +msgid "Hungarianアルゴリズムによってそれらの区画とエージェントを (間の距離の総和が最も小さくなるように)1対1で結びつけます。" +msgstr "Assigns the sections to the agents in a one-to-one manner using the Hungarian algorithm, minimizing the total distance between them." + +#: ../../source/hands-on/clustering.md:16 +msgid "![クラスタリングの画像](./../images/clustering_image.jpg)" +msgstr "![Clustering Image](./../images/clustering_image.jpg)" + +#: ../../source/hands-on/clustering.md:16 +#: ../../source/hands-on/clustering.md:362 +msgid "クラスタリングの画像" +msgstr "Clustering Image" + +#: ../../source/hands-on/clustering.md:18 +msgid "クラスタリングモジュールの実装" +msgstr "Implementation of the Clustering Module" + +#: ../../source/hands-on/clustering.md:21 ../../source/hands-on/search.md:10 +msgid "以降の作業では、カレントディレクトリがプロジェクトのルートディレクトリであることを前提としています。" +msgstr "The following tasks assume that the current directory is the root directory of the project." + +#: ../../source/hands-on/clustering.md:24 +msgid "まず、クラスタリングモジュールを記述するためのファイルを作成します。" +msgstr "First, create a file to write the clustering module." + +#: ../../source/hands-on/clustering.md:31 +msgid "次に、クラスタリングモジュールの実装を行います。 以下のコードを `k_means_pp_clustering.py` に記述してください。" +msgstr "Next, implement the clustering module. Write the following code into `k_means_pp_clustering.py`." + +#: ../../source/hands-on/clustering.md:295 +msgid "k-means++の実装は、scikit-learnの`KMeans`クラスを使用しています。`KMeans`クラスは、`n_clusters`で指定したクラスター数によって地図上のオブジェクトをクラスタリングします。クラスタリング結果は、`labels_`属性に格納されます。また、`cluster_centers_`属性には各クラスターの中心座標が格納されます。" +msgstr "The implementation of k-means++ uses the `KMeans` class from scikit-learn. The `KMeans` class clusters objects on the map based on the number of clusters specified by `n_clusters`. The clustering results are stored in the `labels_` attribute, and the coordinates of each cluster center are stored in the `cluster_centers_` attribute." + +#: ../../source/hands-on/clustering.md:297 +msgid "hungarianアルゴリズムの実装は、scipyの`linear_sum_assignment`関数を使用しています。`linear_sum_assignment`関数は、コスト行列を引数として受け取り、最適な割り当てを行います。" +msgstr "The implementation of the Hungarian algorithm uses the `linear_sum_assignment` function from scipy. The `linear_sum_assignment` function takes a cost matrix as an argument and performs optimal assignment." + +#: ../../source/hands-on/clustering.md:299 +msgid "次に、作成したモジュールを登録します。`config/module.yaml` を以下のように編集してください。" +msgstr "Next, register the created module. Edit `config/module.yaml` as follows." + +#: ../../source/hands-on/clustering.md:310 ../../source/hands-on/search.md:160 +msgid "ターミナルを2つ起動します。" +msgstr "Open two terminals." + +#: ../../source/hands-on/clustering.md:312 ../../source/hands-on/search.md:162 +msgid "片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します:" +msgstr "Open one terminal and start the simulation server with the following command:" + +#: ../../source/hands-on/clustering.md:320 ../../source/hands-on/search.md:170 +msgid "その後、別のターミナルを開き、エージェントを起動します:" +msgstr "Then open another terminal and start the agent:" + +#: ../../source/hands-on/clustering.md:328 +msgid "エージェントが起動すると、標準出力にクラスタリング結果が表示されます。" +msgstr "When the agent starts, the clustering results will be displayed in the standard output." + +#: ../../source/hands-on/clustering.md:335 +msgid "このままだと、クラスタリング結果がわかりにくいので、クラスタリング結果を地図上に表示してみましょう。" +msgstr "As it is, the clustering results are not very clear, so let's display the clustering results on the map." + +#: ../../source/hands-on/clustering.md:337 +msgid "{download}`クラスターの可視化用スクリプト <./../download/cluster_plot.zip>`をダウンロードして解凍し、中の`main.py`の以下の部分に" +msgstr "{download}`Download the cluster visualization script <./../download/cluster_plot.zip>` and extract it. Then, in `main.py`, modify the following part." + +#: ../../source/hands-on/clustering.md:344 +msgid "出力の`Clustered entities: `の後ろの部分の配列をコピーして貼り付けてください。" +msgstr "Copy the array after `Clustered entities:` in the output and paste it." + +#: ../../source/hands-on/clustering.md:346 +msgid "例" +msgstr "Example" + +#: ../../source/hands-on/clustering.md:353 +msgid "貼り付けたら、以下のコマンドを実行してください。" +msgstr "After pasting, execute the following command." + +#: ../../source/hands-on/clustering.md:360 +msgid "以下のような画像が出力されます。" +msgstr "An image like the one below will be output." + +#: ../../source/hands-on/clustering.md:362 +msgid "![クラスタリングの画像](./../images/cluster.png)" +msgstr "![Clustering Image](./../images/cluster.png)" + +#: ../../source/hands-on/search.md:1 +msgid "サーチモジュール" +msgstr "Search Module" + +#: ../../source/hands-on/search.md:3 +msgid "サーチモジュールの概要" +msgstr "Overview of the Search Module" + +#: ../../source/hands-on/search.md:5 +msgid "" +"今回開発するモジュールは、`KMeansPPClustering` モジュールを用いた情報探索対象決定 (`Search`) モジュールです。 " +"クラスタリングモジュールによってエージェント間で担当地域の分割をおこない、 担当地域内からランダムに探索対象として選択します。" +msgstr "" +"The module to be developed this time is the information search target determination (`Search`) module using the `KMeansPPClustering` module. " +"The clustering module divides the areas of responsibility among agents and randomly selects search targets within the assigned areas." + +#: ../../source/hands-on/search.md:7 +msgid "サーチモジュールの実装の準備" +msgstr "Preparation for Implementing the Search Module" + +#: ../../source/hands-on/search.md:13 +msgid "まず、サーチモジュールを記述するためのファイルを作成します。" +msgstr "First, create a file to write the search module." + +#: ../../source/hands-on/search.md:19 +msgid "" +"次に、サーチモジュールの実装を行います。 以下のコードを `k_means_pp_search.py` に記述してください。 " +"これが今回実装するサーチモジュールの雛形になります。" +msgstr "" +"Next, implement the search module. Write the following code into `k_means_pp_search.py`. " +"This will be the template for the search module to be implemented this time." + +#: ../../source/hands-on/search.md:67 +msgid "モジュールの登録" +msgstr "Module Registration" + +#: ../../source/hands-on/search.md:69 +msgid "次に、作成したモジュールを登録します。 以下のように`config/module.yaml`の該当箇所を変更してください" +msgstr "Next, register the created module. Modify the relevant section of `config/module.yaml` as follows." + +#: ../../source/hands-on/search.md:83 +msgid "モジュールの実装" +msgstr "Module Implementation" + +#: ../../source/hands-on/search.md:85 +msgid "まず、`KMeansPPClustering` モジュールを呼び出せるようにします。" +msgstr "First, make it possible to call the `KMeansPPClustering` module." + +#: ../../source/hands-on/search.md:87 +msgid "以下のコードを`config/module.yaml`に追記してください。" +msgstr "Add the following code to `config/module.yaml`." + +#: ../../source/hands-on/search.md:94 +msgid "次に、`KMeansPPSearch` モジュールで `KMeansPPClustering` モジュールを呼び出せるようにします。" +msgstr "Next, make it possible to call the `KMeansPPClustering` module in the `KMeansPPSearch` module." + +#: ../../source/hands-on/search.md:96 ../../source/hands-on/search.md:136 +msgid "以下のコードを `k_means_pp_search.py` に追記してください。" +msgstr "Add the following code to `k_means_pp_search.py`." + +#: ../../source/hands-on/search.md:134 +msgid "そして、`calculate` メソッドでクラスタリングモジュールを呼び出し、探索対象を決定するように変更します。" +msgstr "Then, modify the `calculate` method to call the clustering module and determine the search target." + +#: ../../source/hands-on/search.md:158 +msgid "以上で、`KMeansPPClustering` モジュールを用いた `KMeansPPSearch` モジュールの実装が完了しました。" +msgstr "This completes the implementation of the `KMeansPPSearch` module using the `KMeansPPClustering` module." + +#: ../../source/hands-on/search.md:178 +msgid "モジュールの改善" +msgstr "Module Improvement" + +#: ../../source/hands-on/search.md:180 +msgid "" +"`KMeansPPSearch` モジュールは、クラスタリングモジュールを用いて担当地域内からランダムに探索対象を選択しています。 " +"そのため、以下のような問題があります。" +msgstr "" +"The `KMeansPPSearch` module randomly selects search targets within the assigned areas using the clustering module. " +"As a result, the following issues arise:" + +#: ../../source/hands-on/search.md:183 +msgid "探索対象がステップごとに変わってしまう" +msgstr "The search target changes with each step" + +#: ../../source/hands-on/search.md:184 +msgid "目標にたどり着く前に探索対象が変わってしまうため、なかなか目標にたどり着けない" +msgstr "The search target changes before reaching the goal, making it difficult to reach the goal" + +#: ../../source/hands-on/search.md:185 +msgid "色んなところにランダムに探索対象を選択することで、効率的な探索ができない" +msgstr "Randomly selecting search targets in various places makes efficient searching impossible" + +#: ../../source/hands-on/search.md:186 +msgid "すでに探索したエンティティを再度探索対象として選択してしまうため、効率的な探索ができない" +msgstr "Selecting already searched entities as search targets again makes efficient searching impossible" + +#: ../../source/hands-on/search.md:187 ../../source/hands-on/search.md:329 +msgid "近くに未探索のエンティティがあるのに、遠くのエンティティを探索対象として選択してしまう" +msgstr "Selecting distant entities as search targets even though there are unexplored entities nearby" + +#: ../../source/hands-on/search.md:189 +msgid "などの問題があります。" +msgstr "These are some of the issues." + +#: ../../source/hands-on/search.md:191 +msgid "課題" +msgstr "Challenges" + +#: ../../source/hands-on/search.md:193 +msgid "`KMeansPPSearch` モジュールを改善し、より効率的な探索を行うモジュールを実装して見てください。" +msgstr "Improve the `KMeansPPSearch` module and implement a module that performs more efficient searches." + +#: ../../source/hands-on/search.md:196 +msgid "ここに上げた問題以外にも、改善すべき点が存在すると思うので、それを改善していただいても構いません。" +msgstr "There may be other points for improvement besides the issues listed here, so feel free to address them as well." + +#: ../../source/hands-on/search.md:200 +msgid "プログラム例のプログラムにも一部改善点があるので、余裕があったら修正してみてください。" +msgstr "There are also some points for improvement in the example program, so try to fix them if you have time." + +#: ../../source/hands-on/search.md:203 +msgid "探索対象がステップごとに変わってしまう問題" +msgstr "Issue of the search target changing with each step" + +#: ../../source/hands-on/search.md:205 ../../source/hands-on/search.md:248 +#: ../../source/hands-on/search.md:331 +msgid "方針のヒント" +msgstr "Hints for the Approach" + +#: ../../source/hands-on/search.md:208 +msgid "一度選択した探索対象に到達するまで、探索対象を変更しないようにする" +msgstr "Do not change the search target until the initially selected target is reached" + +#: ../../source/hands-on/search.md:211 ../../source/hands-on/search.md:254 +#: ../../source/hands-on/search.md:337 +msgid "プログラム例" +msgstr "Example Program" + +#: ../../source/hands-on/search.md:246 +msgid "すでに探索したエンティティを再度探索対象として選択してしまう問題" +msgstr "Issue of selecting already searched entities as search targets again" + +#: ../../source/hands-on/search.md:251 +msgid "すでに探索したエンティティを何かしらの方法で記録し、再度探索対象として選択しないようにする" +msgstr "Record already searched entities in some way to avoid selecting them as search targets again" + +#: ../../source/hands-on/search.md:334 +msgid "エンティティ間の距離を計算し、もっとも近いエンティティを探索対象として選択する" +msgstr "Calculate the distance between entities and select the closest entity as the search target" diff --git a/docs/source/locale/en/LC_MESSAGES/index.po b/docs/source/locale/en/LC_MESSAGES/index.po new file mode 100644 index 00000000..56c8eec5 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/index.po @@ -0,0 +1,98 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/index.rst:35 +msgid "インストール" +msgstr "Installation" + +#: ../../source/index.rst:42 +msgid "クイックスタート" +msgstr "Quick Start" + +#: ../../source/index.rst:48 +msgid "チュートリアル" +msgstr "Tutorial" + +#: ../../source/index.rst:58 +msgid "ハンズオン" +msgstr "Hands-On" + +#: ../../source/index.rst:65 +msgid "APIドキュメント" +msgstr "API Documentation" + +#: ../../source/index.rst:7 +msgid "adf-core-pythonのドキュメント" +msgstr "adf-core-python Documentation" + +#: ../../source/index.rst:11 +msgid "現在このパッケージは開発中です。破壊的な変更が行われる可能性があります。" +msgstr "This package is currently under development. Breaking changes may occur." + +#: ../../source/index.rst:15 +msgid "パッケージとしてまだ公開していないため、pip でインストールすることはできません。" +msgstr "Since it has not been released as a package yet, it cannot be installed via pip." + +#: ../../source/index.rst:19 +msgid "以下の言語のドキュメントは機械翻訳を使用しています。翻訳の正確性については保証できません。 英語" +msgstr "The following language documents use machine translation. The accuracy of the translation cannot be guaranteed. English" + +#: ../../source/index.rst:19 +msgid "概要" +msgstr "Overview" + +#: ../../source/index.rst:20 +msgid "" +"adf-core-pythonは、RoboCup Rescue " +"Simulation(RRS)におけるエージェント開発を支援するためのライブラリ及びフレームワークです。 adf-core-" +"pythonを使用することで、エージェントの開発を効率化し、再利用性を向上させることができます。" +msgstr "" +"adf-core-python is a library and framework to support agent development in RoboCup Rescue " +"Simulation (RRS). By using adf-core-python, you can streamline agent development and improve reusability." + +#: ../../source/index.rst:24 +msgid "特徴" +msgstr "Features" + +#: ../../source/index.rst:25 +msgid "adf-core-pythonには以下のような特徴があります。" +msgstr "adf-core-python has the following features:" + +#: ../../source/index.rst:27 +msgid "**モジュール単位での開発**: モジュール単位でエージェント開発を行い、モジュールの入れ替えが容易です。" +msgstr "**Module-based development**: Develop agents on a module basis, making it easy to replace modules." + +#: ../../source/index.rst:28 +msgid "**モジュールの再利用**: 他のエージェントで使用されているモジュールを再利用することができます。" +msgstr "**Module reuse**: Reuse modules used by other agents." + +#: ../../source/index.rst:29 +msgid "**エージェントの開発に集中**: シミュレーションサーバーとの通信やログ出力などの共通処理をライブラリが提供します。" +msgstr "**Focus on agent development**: The library provides common processing such as communication with the simulation server and log output." + +#: ../../source/index.rst:32 +msgid "はじめに" +msgstr "Getting Started" + +#: ../../source/index.rst:33 +msgid "adf-core-pythonを始めるには、インストールに従い、このドキュメントに記載されているチュートリアルやハンズオンを参照してください。" +msgstr "To get started with adf-core-python, follow the installation and refer to the tutorials and hands-on in this document." diff --git a/docs/source/locale/en/LC_MESSAGES/install.po b/docs/source/locale/en/LC_MESSAGES/install.po new file mode 100644 index 00000000..9e2e4c3b --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/install.po @@ -0,0 +1,387 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/install/environment/environment.md:1 +msgid "環境構築" +msgstr "Environment Setup" + +#: ../../source/install/environment/environment.md:3 +msgid "" +"adf-core-pythonをインストールするには以下の必要条件が必要です。 " +"既にお使いのPCにインストールされている場合は再度インストールする必要はありません。" +msgstr "" +"To install adf-core-python, the following prerequisites are required. " +"If they are already installed on your PC, you do not need to reinstall them." + +#: ../../source/install/environment/environment.md:6 +msgid "必要条件" +msgstr "Prerequisites" + +#: ../../source/install/environment/environment.md:8 +msgid "Git" +msgstr "Git" + +#: ../../source/install/environment/environment.md:9 +#: ../../source/install/install/install.md:9 +msgid "Python 3.12 以上" +msgstr "Python 3.12 or higher" + +#: ../../source/install/environment/environment.md:10 +msgid "OpenJDK 17" +msgstr "OpenJDK 17" + +#: ../../source/install/environment/environment.md:12 +msgid "各OSでのインストール方法は以下のページをそれぞれ参照してください" +msgstr "Refer to the following pages for installation methods for each OS" + +#: ../../source/install/environment/environment.md:14 +msgid "[Windowsでの必要条件のインストール方法](./windows/install.md)" +msgstr "[How to install prerequisites on Windows](./windows/install.md)" + +#: ../../source/install/environment/environment.md:16 +msgid "[MacOSでの必要条件のインストール方法](./mac/install.md)" +msgstr "[How to install prerequisites on MacOS](./mac/install.md)" + +#: ../../source/install/environment/environment.md:18 +msgid "[Linuxでの必要条件のインストール方法](./linux/install.md)" +msgstr "[How to install prerequisites on Linux](./linux/install.md)" + +#: ../../source/install/environment/environment.md:20 +msgid "シミュレーションサーバーのインストール" +msgstr "Installing the Simulation Server" + +#: ../../source/install/environment/environment.md:22 +msgid "次にRoboCup Rescue Simulationのシミュレーションサーバーをインストールします。" +msgstr "Next, install the RoboCup Rescue Simulation server." + +#: ../../source/install/environment/environment.md:25 +msgid "WORKING_DIR は任意のディレクトリを作成、指定してください。" +msgstr "Create and specify any directory as WORKING_DIR." + +#: ../../source/install/environment/environment.md:36 +msgid "ビルドした際に以下のようなメッセージが表示されたら成功です。" +msgstr "If the following message is displayed when you build, it is successful." + +#: ../../source/install/environment/environment.md:42 +msgid "シミュレーションサーバーの動作確認" +msgstr "Simulation Server Operation Check" + +#: ../../source/install/environment/environment.md:49 +msgid "![シミュレーションサーバーの起動](../../images/launch_server.png)" +msgstr "![Launching the Simulation Server](../../images/launch_server.png)" + +#: ../../source/install/environment/environment.md:49 +msgid "シミュレーションサーバーの起動" +msgstr "Launching the Simulation Server" + +#: ../../source/install/environment/environment.md:51 +msgid "" +"上記のように何個かのウィンドウが表示されたら成功です。 コマンドラインで `Ctrl + C` (MacOSの場合は `Command + C`" +" ) を押すとシミュレーションサーバーが終了します。" +msgstr "" +"If several windows are displayed as shown above, it is successful. Press `Ctrl + C` (or `Command + C` on MacOS) in the command line to stop the simulation server." + +#: ../../source/install/environment/environment.md:55 +msgid "シミュレーションサーバーを停止させたあとは、プロセスが残ってしまう場合があるので`./kill.sh` を実行してください。" +msgstr "After stopping the simulation server, run `./kill.sh` as there may be remaining processes." + +#: ../../source/install/environment/linux/install.md:1 +msgid "Linuxでの環境構築" +msgstr "Environment Setup on Linux" + +#: ../../source/install/environment/linux/install.md:3 +#: ../../source/install/environment/windows/install.md:3 +msgid "1. Gitのインストール" +msgstr "1. Installing Git" + +#: ../../source/install/environment/linux/install.md:5 +#: ../../source/install/environment/mac/install.md:3 +#: ../../source/install/environment/mac/install.md:20 +msgid "Terminalを起動し、以下のコマンドを実行します。" +msgstr "Open the terminal and run the following command." + +#: ../../source/install/environment/linux/install.md:10 +msgid "もし、`command not found`などのエラーが出た場合、OS標準のパッケージマネージャーを使用してインストールします。" +msgstr "If you get an error like `command not found`, use the OS's standard package manager to install." + +#: ../../source/install/environment/linux/install.md:11 +#: ../../source/install/environment/linux/install.md:85 +#: ../../source/install/environment/linux/install.md:122 +msgid "DebianベースのOSの場合(Ubuntuなど)" +msgstr "For Debian-based OS (e.g., Ubuntu)" + +#: ../../source/install/environment/linux/install.md:17 +#: ../../source/install/environment/linux/install.md:91 +#: ../../source/install/environment/linux/install.md:128 +msgid "Red HatベースのOSの場合(Fedoraなど)" +msgstr "For Red Hat-based OS (e.g., Fedora)" + +#: ../../source/install/environment/linux/install.md:26 +#: ../../source/install/environment/linux/install.md:109 +#: ../../source/install/environment/linux/install.md:137 +#: ../../source/install/environment/mac/install.md:28 +#: ../../source/install/environment/mac/install.md:43 +#: ../../source/install/environment/mac/install.md:58 +msgid "以下のコマンドを入力し、バージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください)" +msgstr "Enter the following command, and if the version is displayed, it is successful. (If not displayed, restart the terminal.)" + +#: ../../source/install/environment/linux/install.md:31 +#: ../../source/install/environment/windows/install.md:14 +msgid "2. Pythonのインストール" +msgstr "2. Installing Python" + +#: ../../source/install/environment/linux/install.md:33 +#: ../../source/install/environment/mac/install.md:35 +msgid "Terminalを起動し、以下のコマンドを実行します。また、バージョンが3.12以上になっていることを確認します。" +msgstr "Open the terminal and run the following command. Also, make sure the version is 3.12 or higher." + +#: ../../source/install/environment/linux/install.md:38 +msgid "" +"もし、`command not " +"found`などのエラーが出た場合やバージョンが低い場合、Pythonのバージョン管理ツールであるpyenvを使用してインストールします" +msgstr "" +"If you get an error like `command not found` or the version is low, use the Python version management tool pyenv to install." + +#: ../../source/install/environment/linux/install.md:41 +msgid "インストール方法の内容が最新ではない場合があるため、[https://github.com/pyenv/pyenv](https://github.com/pyenv/pyenv)を参照してください。" +msgstr "The installation method may not be up to date, so please refer to [https://github.com/pyenv/pyenv](https://github.com/pyenv/pyenv)." + +#: ../../source/install/environment/linux/install.md:44 +msgid "以下のコマンドを実行します。" +msgstr "Run the following command." + +#: ../../source/install/environment/linux/install.md:49 +msgid "次に以下のコマンドを実行して、使用しているShellを確認します。" +msgstr "Next, run the following command to check the shell you are using." + +#: ../../source/install/environment/linux/install.md:54 +msgid "表示されたShellに従ってコマンドを実行してください。" +msgstr "Follow the commands according to the displayed shell." + +#: ../../source/install/environment/linux/install.md:56 +msgid "`bash`が表示された方は以下のコマンドを実行してください" +msgstr "If `bash` is displayed, run the following command." + +#: ../../source/install/environment/linux/install.md:70 +msgid "`zsh`が表示された方は以下のコマンドを実行してください" +msgstr "If `zsh` is displayed, run the following command." + +#: ../../source/install/environment/linux/install.md:77 +msgid "`fish`が表示された方は以下のコマンドを実行してください" +msgstr "If `fish` is displayed, run the following command." + +#: ../../source/install/environment/linux/install.md:84 +msgid "必要パッケージのインストール" +msgstr "Installing necessary packages" + +#: ../../source/install/environment/linux/install.md:100 +msgid "python3.12のインストール" +msgstr "Installing python3.12" + +#: ../../source/install/environment/linux/install.md:114 +#: ../../source/install/environment/mac/install.md:48 +#: ../../source/install/environment/windows/install.md:24 +msgid "3. OpenJDKのインストール" +msgstr "3. Installing OpenJDK" + +#: ../../source/install/environment/linux/install.md:116 +#: ../../source/install/environment/mac/install.md:50 +msgid "Terminalを起動し、以下のコマンドを実行します。また、バージョンが17になっていることを確認します。" +msgstr "Open the terminal and run the following command. Also, make sure the version is 17." + +#: ../../source/install/environment/linux/install.md:121 +msgid "" +"もし、`command not " +"found`などのエラーが出た場合やバージョンが異なる場合、OS標準のパッケージマネージャーを使用してインストールします" +msgstr "" +"If you get an error like `command not found` or the version is different, use the OS's standard package manager to install." + +#: ../../source/install/environment/mac/install.md:1 +msgid "Macでの環境構築" +msgstr "Environment Setup on Mac" + +#: ../../source/install/environment/mac/install.md:2 +msgid "1. Homebrewのインストール" +msgstr "1. Installing Homebrew" + +#: ../../source/install/environment/mac/install.md:8 +#: ../../source/install/environment/mac/install.md:24 +msgid "もし、`command not found`などのエラーが出た場合、以下のコマンドを実行します。" +msgstr "If you get an error like `command not found`, run the following command." + +#: ../../source/install/environment/mac/install.md:13 +msgid "もう一度、以下のコマンドを実行してバージョンが表示されたら成功です。(表示されない方はTerminalを再起動してください)" +msgstr "Run the following command again, and if the version is displayed, it is successful. (If not displayed, restart the terminal.)" + +#: ../../source/install/environment/mac/install.md:18 +msgid "2. Gitのインストール" +msgstr "2. Installing Git" + +#: ../../source/install/environment/mac/install.md:33 +msgid "3. Pythonのインストール" +msgstr "3. Installing Python" + +#: ../../source/install/environment/mac/install.md:39 +msgid "もし、`command not found`などのエラーが出た場合やバージョンが低い場合、以下のコマンドを実行します。" +msgstr "If you get an error like `command not found` or the version is low, run the following command." + +#: ../../source/install/environment/mac/install.md:54 +msgid "もし、`command not found`などのエラーが出た場合やバージョンが異なる場合、以下のコマンドを実行します。" +msgstr "If you get an error like `command not found` or the version is different, run the following command." + +#: ../../source/install/environment/windows/install.md:1 +msgid "Windowsでの環境構築" +msgstr "Environment Setup on Windows" + +#: ../../source/install/environment/windows/install.md:5 +msgid "[Git for Windows](https://gitforwindows.org/)の公式サイトにアクセスします。" +msgstr "Access the [Git for Windows](https://gitforwindows.org/) official site." + +#: ../../source/install/environment/windows/install.md:6 +msgid "トップページの\"Download\"をクリックします" +msgstr "Click \"Download\" on the top page." + +#: ../../source/install/environment/windows/install.md:7 +#: ../../source/install/environment/windows/install.md:18 +msgid "ダウンロードが完了した後、インストーラーを実行します。" +msgstr "After the download is complete, run the installer." + +#: ../../source/install/environment/windows/install.md:8 +msgid "全て\"Next\"をクリックします。" +msgstr "Click \"Next\" for all steps." + +#: ../../source/install/environment/windows/install.md:9 +#: ../../source/install/environment/windows/install.md:20 +msgid "インストールが完了するまで待ちます。" +msgstr "Wait for the installation to complete." + +#: ../../source/install/environment/windows/install.md:10 +msgid "インストールが完了したら\"Finish\"をクリックします。" +msgstr "Click \"Finish\" when the installation is complete." + +#: ../../source/install/environment/windows/install.md:11 +msgid "検索バーに\"Git Bash\"と入力し、Git Bashを実行します。" +msgstr "Enter \"Git Bash\" in the search bar and run Git Bash." + +#: ../../source/install/environment/windows/install.md:12 +msgid "画面が表示されていたらインストール成功です。" +msgstr "If the screen is displayed, the installation is successful." + +#: ../../source/install/environment/windows/install.md:16 +msgid "[Python](https://www.python.org/)の公式サイトにアクセスします。" +msgstr "Access the [Python](https://www.python.org/) official site." + +#: ../../source/install/environment/windows/install.md:17 +msgid "トップページの\"Download Python ~\"をクリックします" +msgstr "Click \"Download Python ~\" on the top page." + +#: ../../source/install/environment/windows/install.md:19 +msgid "\"Add python.exe to PATH\"にチェックが入っていることを確認した後、\"Install Now\"をクリックします。" +msgstr "Make sure \"Add python.exe to PATH\" is checked, then click \"Install Now\"." + +#: ../../source/install/environment/windows/install.md:21 +msgid "インストールが完了したら\"Close\"をクリックします。" +msgstr "Click \"Close\" when the installation is complete." + +#: ../../source/install/environment/windows/install.md:22 +msgid "" +"Git Bashを開き、`python --version`と入力し、`Python " +"[バージョン]`が表示されたら成功です。(もし表示されない場合はGit Bashを開き直してください)" +msgstr "" +"Open Git Bash, enter `python --version`, and if `Python [version]` is displayed, it is successful. (If not displayed, reopen Git Bash.)" + +#: ../../source/install/environment/windows/install.md:26 +msgid "[OpenJDK](https://jdk.java.net/archive/)のダウンロードページにアクセスします。" +msgstr "Access the [OpenJDK](https://jdk.java.net/archive/) download page." + +#: ../../source/install/environment/windows/install.md:27 +msgid "17.0.2のWindowsの横にある\"zip\"をクリックします。" +msgstr "Click \"zip\" next to Windows for version 17.0.2." + +#: ../../source/install/environment/windows/install.md:28 +msgid "ダウンロードしたzipを展開(解凍)します。" +msgstr "Extract the downloaded zip file." + +#: ../../source/install/environment/windows/install.md:29 +msgid "展開(解凍)すると\"jdk-17.0.2\"のようなフォルダができるのを確認します。" +msgstr "Confirm that a folder like \"jdk-17.0.2\" is created after extraction." + +#: ../../source/install/environment/windows/install.md:30 +msgid "このフォルダ\"jdk-17.0.2\"を`C:¥`の直下に移動させます。" +msgstr "Move this folder \"jdk-17.0.2\" to the root of `C:¥`." + +#: ../../source/install/environment/windows/install.md:31 +msgid "Windowsでコマンドプロンプトを管理者として実行します。" +msgstr "Run the command prompt as an administrator on Windows." + +#: ../../source/install/environment/windows/install.md:32 +msgid "開いたら以下のコマンドを実行します。" +msgstr "Once opened, run the following command." + +#: ../../source/install/environment/windows/install.md:36 +msgid "次に以下のコマンドを実行します。" +msgstr "Next, run the following command." + +#: ../../source/install/environment/windows/install.md:40 +msgid "Git Bashを開き、`java -version`と入力します。 以下のような文字が表示されたらインストール成功です。" +msgstr "Open Git Bash and enter `java -version`. If the following text is displayed, the installation is successful." + +#: ../../source/install/install/install.md:1 +msgid "インストール" +msgstr "Installation" + +#: ../../source/install/install/install.md:3 +msgid "このセクションでは、パッケージのインストール方法について説明します。" +msgstr "This section explains how to install the package." + +#: ../../source/install/install/install.md:5 +msgid "前提条件" +msgstr "Prerequisites" + +#: ../../source/install/install/install.md:7 +msgid "インストールする前に、以下の前提条件を確認してください:" +msgstr "Before installing, please check the following prerequisites:" + +#: ../../source/install/install/install.md:10 +msgid "pip" +msgstr "pip" + +#: ../../source/install/install/install.md:12 +msgid "パッケージのインストール" +msgstr "Installing the package" + +#: ../../source/install/install/install.md:14 +msgid "パッケージをインストールするには、次のコマンドを実行します:" +msgstr "To install the package, run the following command:" + +#: ../../source/install/install/install.md:20 +msgid "インストールの確認" +msgstr "Verifying the installation" + +#: ../../source/install/install/install.md:22 +msgid "インストールを確認するには、次のコマンドを実行します:" +msgstr "To verify the installation, run the following command:" + +#: ../../source/install/install/install.md:28 +msgid "パッケージが正しくインストールされている場合、パッケージのバージョン番号などが表示されます。" +msgstr "If the package is installed correctly, the package version number, etc., will be displayed." + diff --git a/docs/source/locale/en/LC_MESSAGES/modules.po b/docs/source/locale/en/LC_MESSAGES/modules.po new file mode 100644 index 00000000..49c7a03a --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/modules.po @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/modules.rst:2 +msgid "adf_core_python" +msgstr "adf_core_python" + diff --git a/docs/source/locale/en/LC_MESSAGES/quickstart.po b/docs/source/locale/en/LC_MESSAGES/quickstart.po new file mode 100644 index 00000000..af56f571 --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/quickstart.po @@ -0,0 +1,77 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/quickstart/quickstart.md:1 +msgid "クイックスタート" +msgstr "Quick Start" + +#: ../../source/quickstart/quickstart.md:3 +msgid "前提条件" +msgstr "Prerequisites" + +#: ../../source/quickstart/quickstart.md:5 +msgid "インストールする前に、以下の前提条件を確認してください:" +msgstr "Before installing, please check the following prerequisites:" + +#: ../../source/quickstart/quickstart.md:7 +msgid "Python 3.12 以上" +msgstr "Python 3.12 or higher" + +#: ../../source/quickstart/quickstart.md:8 +msgid "pip" +msgstr "pip" + +#: ../../source/quickstart/quickstart.md:10 +msgid "パッケージのインストール" +msgstr "Package Installation" + +#: ../../source/quickstart/quickstart.md:12 +msgid "パッケージをインストールするには、次のコマンドを実行します:" +msgstr "To install the package, run the following command:" + +#: ../../source/quickstart/quickstart.md:18 +msgid "新規エージェントの作成" +msgstr "Creating a New Agent" + +#: ../../source/quickstart/quickstart.md:20 +msgid "新規エージェントを作成するには、次のコマンドを実行します:" +msgstr "To create a new agent, run the following command:" + +#: ../../source/quickstart/quickstart.md:26 +msgid "実行すると、下記のような対話形式のプロンプトが表示されます:" +msgstr "When executed, an interactive prompt like the one below will be displayed:" + +#: ../../source/quickstart/quickstart.md:33 +msgid "入力後、下記のようなエージェントのテンプレートがカレントディレクトリに作成されます。" +msgstr "After input, an agent template like the one below will be created in the current directory." + +#: ../../source/quickstart/quickstart.md:55 +msgid "エージェントを実行する" +msgstr "Running the Agent" + +#: ../../source/quickstart/quickstart.md:57 +msgid "エージェントを実行するには、シミュレーションサーバーを起動し、次のコマンドを実行します:" +msgstr "To run the agent, start the simulation server and run the following command:" + +#: ../../source/quickstart/quickstart.md:63 +msgid "エージェントの実行が開始され、シミュレーションサーバーとの通信が開始されます。" +msgstr "The agent execution will start, and communication with the simulation server will begin." diff --git a/docs/source/locale/en/LC_MESSAGES/tutorial.po b/docs/source/locale/en/LC_MESSAGES/tutorial.po new file mode 100644 index 00000000..8aa509ea --- /dev/null +++ b/docs/source/locale/en/LC_MESSAGES/tutorial.po @@ -0,0 +1,626 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024, Haruki Uehara, Yuki Shimada +# This file is distributed under the same license as the adf-core-python +# package. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: adf-core-python \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 13:59+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/tutorial/agent/agent.md:1 +msgid "エージェントの作成" +msgstr "Creating an Agent" + +#: ../../source/tutorial/agent/agent.md:3 +msgid "このセクションでは、`adf-core-python` ライブラリの使用方法の概要を提供します。" +msgstr "This section provides an overview of how to use the `adf-core-python` library." + +#: ../../source/tutorial/agent/agent.md:5 +msgid "新規エージェントの作成" +msgstr "Creating a New Agent" + +#: ../../source/tutorial/agent/agent.md:7 +msgid "新規エージェントを作成するには、次のコマンドを実行します:" +msgstr "To create a new agent, run the following command:" + +#: ../../source/tutorial/agent/agent.md:13 +msgid "実行すると、下記のような対話形式のプロンプトが表示されます:" +msgstr "When executed, an interactive prompt like the one below will be displayed:" + +#: ../../source/tutorial/agent/agent.md:21 +msgid "" +"エージェントチーム名は、エージェントのディレクトリ名として使用されます。 以後、エージェントチーム名を `` " +"として参照します。" +msgstr "" +"The agent team name will be used as the directory name for the agent. Hereafter, the agent team name will be referred to as ``." + +#: ../../source/tutorial/agent/agent.md:25 +msgid "入力後、下記のようなエージェントのテンプレートがカレントディレクトリに作成されます。" +msgstr "After entering, an agent template like the one below will be created in the current directory." + +#: ../../source/tutorial/agent/agent.md:47 +msgid "シミュレーションを実行する" +msgstr "Running the Simulation" + +#: ../../source/tutorial/agent/agent.md:49 +#: ../../source/tutorial/agent/agent_control.md:110 +#: ../../source/tutorial/agent/agent_control.md:330 +msgid "ターミナルを2つ起動します。" +msgstr "Open two terminals." + +#: ../../source/tutorial/agent/agent.md:51 +#: ../../source/tutorial/agent/agent_control.md:112 +#: ../../source/tutorial/agent/agent_control.md:332 +msgid "片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します:" +msgstr "Open one terminal and start the simulation server with the following command:" + +#: ../../source/tutorial/agent/agent.md:59 +#: ../../source/tutorial/agent/agent_control.md:120 +#: ../../source/tutorial/agent/agent_control.md:340 +msgid "その後、別のターミナルを開き、エージェントを起動します:" +msgstr "Then, open another terminal and start the agent:" + +#: ../../source/tutorial/agent/agent.md:67 +msgid "" +"エージェントが正常に起動すると、シミュレーションサーバーに接続され、エージェントがシミュレーションに参加し、エージェントが動き出します。 " +"途中で止めたい場合は、それぞれのコマンドラインで `Ctrl + C` (MacOSの場合は `Command + C` ) を押してください。" +msgstr "" +"When the agent starts successfully, it will connect to the simulation server, participate in the simulation, and start moving. " +"If you want to stop it midway, press `Ctrl + C` (or `Command + C` on MacOS) in each command line." + +#: ../../source/tutorial/agent/agent.md:71 +msgid "シミュレーションサーバーを停止させたあとは、プロセスが残ってしまう場合があるので`./kill.sh` を実行してください。" +msgstr "After stopping the simulation server, run `./kill.sh` as there may be remaining processes." + +#: ../../source/tutorial/agent/agent_control.md:1 +msgid "エージェントの制御" +msgstr "Agent Control" + +#: ../../source/tutorial/agent/agent_control.md:3 +msgid "このセクションでは、エージェントの制御用のプログラムを作成する方法について説明します。" +msgstr "This section explains how to create a program to control agents." + +#: ../../source/tutorial/agent/agent_control.md:5 +msgid "エージェントの制御について" +msgstr "About Agent Control" + +#: ../../source/tutorial/agent/agent_control.md:7 +msgid "RRSの災害救助エージェントは3種類あり、種類毎にそれぞれ異なるプログラムを書く必要があります。しかし、初めから全てのプログラムを書くことは困難です。ここではまず初めに、消防隊エージェントを操作するプログラムの一部を書いてみましょう。" +msgstr "There are three types of disaster rescue agents in RRS, and each type requires a different program. However, it is difficult to write all the programs from the beginning. Here, let's first write part of the program to control the fire brigade agent." + +#: ../../source/tutorial/agent/agent_control.md:10 +msgid "" +"エージェントを操作するプログラムは、エージェントの種類毎に同一です。 プログラムは各エージェントに配られ、そのエージェントのみの操作を担います。 " +"消防隊エージェントを操作するプログラムを書けば、それがすべての消防隊エージェント上でそれぞれ動作します。" +msgstr "" +"The program that controls the agents is the same for each type of agent. The program is distributed to each agent and is responsible for controlling only that agent. " +"If you write a program to control the fire brigade agent, it will run on all fire brigade agents." + +#: ../../source/tutorial/agent/agent_control.md:13 +msgid "![消防隊エージェント](./../../images/agent_control.jpg)" +msgstr "![Fire Brigade Agent](./../../images/agent_control.jpg)" + +#: ../../source/tutorial/agent/agent_control.md:13 +msgid "消防隊エージェント" +msgstr "Fire Brigade Agent" + +#: ../../source/tutorial/agent/agent_control.md:15 +msgid "エージェントの動作フロー" +msgstr "Agent Operation Flow" + +#: ../../source/tutorial/agent/agent_control.md:17 +msgid "エージェントの動作を決めているのはTacticsというプログラムです。" +msgstr "The program that determines the agent's actions is called Tactics." + +#: ../../source/tutorial/agent/agent_control.md:19 +msgid "" +"消防隊の思考ルーチンは下の図の通りにおおよそおこなわれます。 消防隊の動作としては、まず救助対象の市民を捜索し(`Human " +"Detector`)、見つかった市民を救助します(`Action Rescue`)。 " +"救助対象の市民が見つからない場合は、探索場所を変更して市民を捜索するため、次の捜索場所を決定して(`Search`)移動します(`Action " +"Ext move`)。 " +"なお、エージェントは1ステップ内で、移動と救助活動を同時におこなうことが出来ません。つまり、ステップごとに更新される自身の周辺情報を確認して、動作の対象と動作内容を決定していくということになります。" +" これらそれぞれの機能が、モジュールと呼ばれるプログラムとして分割して表現されています。" +msgstr "" +"The fire brigade's thought routine is roughly as shown in the diagram below. The fire brigade's actions are to first search for the target citizen (`Human Detector`) and rescue the found citizen (`Action Rescue`). " +"If no target citizen is found, the search location is changed to search for citizens, the next search location is determined (`Search`), and the agent moves (`Action Ext move`). " +"Note that the agent cannot perform both movement and rescue activities within one step. In other words, the agent checks the updated surrounding information at each step and determines the target and action content. " +"Each of these functions is expressed as a program called a module." + +#: ../../source/tutorial/agent/agent_control.md:21 +msgid "今回はこの中で、救助(掘り起こし)対象を決定する `Human Detector` モジュールを開発します。" +msgstr "This time, we will develop the `Human Detector` module that determines the rescue (digging) target." + +#: ../../source/tutorial/agent/agent_control.md:23 +msgid "![消防隊エージェントの動作フロー](./../../images/agent_flow.png)" +msgstr "![Fire Brigade Agent Operation Flow](./../../images/agent_flow.png)" + +#: ../../source/tutorial/agent/agent_control.md:23 +msgid "消防隊エージェントの動作フロー" +msgstr "Fire Brigade Agent Operation Flow" + +#: ../../source/tutorial/agent/agent_control.md:25 +msgid "`Human Detector` モジュールの実装の準備" +msgstr "Preparing to Implement the `Human Detector` Module" + +#: ../../source/tutorial/agent/agent_control.md:27 +msgid "まず、`Human Detector` モジュールを記述するためのファイルを作成します。" +msgstr "First, create a file to describe the `Human Detector` module." + +#: ../../source/tutorial/agent/agent_control.md:34 +msgid "" +"次に、`Human Detector` モジュールの雛形の実装を行います。 " +"以下のコードを`fire_brigade_human_detector.py` に記述してください。" +msgstr "" +"Next, implement the template for the `Human Detector` module. " +"Please write the following code in `fire_brigade_human_detector.py`." + +#: ../../source/tutorial/agent/agent_control.md:92 +msgid "モジュールの登録" +msgstr "Registering the Module" + +#: ../../source/tutorial/agent/agent_control.md:94 +msgid "次に、作成したモジュールを登録します。" +msgstr "Next, register the created module." + +#: ../../source/tutorial/agent/agent_control.md:96 +msgid "`WORKING_DIR//config/module.yaml` ファイルを開き、以下の部分を" +msgstr "Open the `WORKING_DIR//config/module.yaml` file and" + +#: ../../source/tutorial/agent/agent_control.md:103 +msgid "以下のように変更してください。" +msgstr "change the following part as shown below." + +#: ../../source/tutorial/agent/agent_control.md:128 +msgid "標準出力に `Calculate FireBrigadeHumanDetector` と表示されれば成功です。" +msgstr "If `Calculate FireBrigadeHumanDetector` is displayed in the standard output, it is successful." + +#: ../../source/tutorial/agent/agent_control.md:130 +msgid "`Human Detector` モジュールの設計" +msgstr "Designing the `Human Detector` Module" + +#: ../../source/tutorial/agent/agent_control.md:132 +msgid "救助対象選択プログラムの中身を書き、消防隊エージェントが救助活動をおこなえるように修正します。" +msgstr "Write the content of the rescue target selection program and modify it so that the fire brigade agent can perform rescue activities." + +#: ../../source/tutorial/agent/agent_control.md:134 +msgid "" +"消防隊エージェントの救助対象を最も簡単に選択する方法は、埋没している市民の中で最も自身に近い市民を選択する方法です。 " +"今回は、この方法を採用した消防隊エージェントの行動対象決定モジュールを書いてみましょう。" +msgstr "" +"The simplest way for a fire brigade agent to select a rescue target is to choose the closest buried citizen. " +"This time, let's write a module to determine the action target of the fire brigade agent using this method." + +#: ../../source/tutorial/agent/agent_control.md:136 +msgid "埋没している市民の中で最も自身に近い市民を救助対象として選択する方法は、フローチャートで表すと下図のようになります。" +msgstr "" +"The method of selecting the closest buried citizen as the rescue target is shown in the flowchart below." + +#: ../../source/tutorial/agent/agent_control.md:138 +msgid "![救助対象選択フローチャート](./../../images/human_detector_flow.png)" +msgstr "![Rescue Target Selection Flowchart](./../../images/human_detector_flow.png)" + +#: ../../source/tutorial/agent/agent_control.md:138 +msgid "救助対象選択フローチャート" +msgstr "Rescue Target Selection Flowchart" + +#: ../../source/tutorial/agent/agent_control.md:140 +msgid "" +"このフローチャート中の各処理を、次小節で紹介する各クラス・メソッド等で置き換えたものを、`fire_brigade_human_detector.py`" +" に記述していくことで、救助対象選択プログラムを完成させます。" +msgstr "" +"By replacing each process in this flowchart with the classes and methods introduced in the next subsection, and writing them in `fire_brigade_human_detector.py`, you will complete the rescue target selection program." + +#: ../../source/tutorial/agent/agent_control.md:142 +msgid "`Human Detector` モジュールの実装で使用するクラス・メソッド" +msgstr "Classes and Methods Used in the Implementation of the `Human Detector` Module" + +#: ../../source/tutorial/agent/agent_control.md:144 +msgid "Entity" +msgstr "Entity" + +#: ../../source/tutorial/agent/agent_control.md:146 +msgid "`Entity` クラスは、エンティティの基底クラスです。 このクラスは、エンティティの基本情報を保持します。" +msgstr "" +"The `Entity` class is the base class for entities. This class holds the basic information of an entity." + +#: ../../source/tutorial/agent/agent_control.md:148 +msgid "" +"RRS上のエンティティは下図のように `Entity` を継承したクラスで表現されています。 " +"赤枠で囲まれたクラスは、クラスの意味がそのままRRSの直接的な構成要素を表しています。" +msgstr "" +"Entities in RRS are represented by classes that inherit from `Entity` as shown in the diagram below. " +"The classes enclosed in red boxes directly represent the components of RRS." + +#: ../../source/tutorial/agent/agent_control.md:150 +msgid "例: Road クラスのインスタンスの中には、 Hydrant クラスを継承してない通常の道路を表すものも存在しています。" +msgstr "" +"For example, among the instances of the Road class, there are those that represent ordinary roads that do not inherit from the Hydrant class." + +#: ../../source/tutorial/agent/agent_control.md:152 +msgid "![エンティティの継承関係](./../../images/entity.png)" +msgstr "![Entity Inheritance Relationship](./../../images/entity.png)" + +#: ../../source/tutorial/agent/agent_control.md:152 +msgid "エンティティの継承関係" +msgstr "Entity Inheritance Relationship" + +#: ../../source/tutorial/agent/agent_control.md:154 +msgid "EntityID" +msgstr "EntityID" + +#: ../../source/tutorial/agent/agent_control.md:156 +msgid "" +"`EntityID` クラスは、全てのエージェント/オブジェクトを一意に識別するためのID(識別子)を表すクラスです。 " +"RRSではエージェントとオブジェクトをまとめて、エンティティと呼んでいます。" +msgstr "" +"The `EntityID` class represents an ID (identifier) that uniquely identifies all agents/objects. " +"In RRS, agents and objects are collectively referred to as entities." + +#: ../../source/tutorial/agent/agent_control.md:158 +msgid "Civilian" +msgstr "Civilian" + +#: ../../source/tutorial/agent/agent_control.md:160 +msgid "`Civilian` クラスは、市民を表すクラスです。このクラスからは、エージェントの位置や負傷の進行状況を取得することができます。" +msgstr "" +"The `Civilian` class represents a citizen. From this class, you can obtain the position of the agent and the progress of injuries." + +#: ../../source/tutorial/agent/agent_control.md:162 +msgid "`entity` が市民であるかどうかを判定する" +msgstr "Determine if `entity` is a civilian" + +#: ../../source/tutorial/agent/agent_control.md:168 +msgid "エンティティIDを取得する" +msgstr "Get the Entity ID" + +#: ../../source/tutorial/agent/agent_control.md:174 +msgid "市民が生きているかどうかを判定する" +msgstr "Determine if the civilian is alive" + +#: ../../source/tutorial/agent/agent_control.md:182 +msgid "市民が埋まっているかどうかを判定する" +msgstr "Determine if the civilian is buried" + +#: ../../source/tutorial/agent/agent_control.md:190 +msgid "WorldInfo" +msgstr "WorldInfo" + +#: ../../source/tutorial/agent/agent_control.md:192 +msgid "" +"`WorldInfo` クラスは、エージェントが把握している情報とそれに関する操作をおこなうメソッドをもったクラスです。 " +"エージェントはこのクラスのインスタンスを通して、他のエージェントやオブジェクトの状態を確認します。" +msgstr "" +"The `WorldInfo` class is a class that has methods for the information that the agent knows and the operations related to it. " +"Through an instance of this class, the agent checks the status of other agents and objects." + +#: ../../source/tutorial/agent/agent_control.md:194 +msgid "モジュール内では、`WorldInfo` クラスのインスタンスを `self._world_info` として保持しています。" +msgstr "" +"In the module, an instance of the `WorldInfo` class is held as `self._world_info`." + +#: ../../source/tutorial/agent/agent_control.md:196 +msgid "エンティティIDからエンティティを取得する" +msgstr "Get the entity from the Entity ID" + +#: ../../source/tutorial/agent/agent_control.md:202 +msgid "指定したクラスのエンティティを全て取得する" +msgstr "Get all entities of the specified class" + +#: ../../source/tutorial/agent/agent_control.md:208 +msgid "エージェントの位置から指定したエンティティまでの距離を取得する" +msgstr "Get the distance from the agent's position to the specified entity" + +#: ../../source/tutorial/agent/agent_control.md:214 +#: ../../source/tutorial/agent/agent_control.md:228 +msgid "[詳細はこちら](../../adf_core_python.core.agent.info.rst)" +msgstr "[Details here](../../adf_core_python.core.agent.info.rst)" + +#: ../../source/tutorial/agent/agent_control.md:216 +msgid "AgentInfo" +msgstr "AgentInfo" + +#: ../../source/tutorial/agent/agent_control.md:218 +msgid "" +"`AgentInfo` クラスは、エージェント自身の情報とそれに関する操作をおこなうメソッドをもったクラスです。 " +"エージェントはこのクラスのインスタンスを通して、エージェント自身の状態を取得します。" +msgstr "" +"The `AgentInfo` class is a class that has methods for the agent's own information and operations related to it. " +"Through an instance of this class, the agent obtains its own status." + +#: ../../source/tutorial/agent/agent_control.md:220 +msgid "モジュール内では、`AgentInfo` クラスのインスタンスを `self._agent_info` として保持しています。" +msgstr "" +"In the module, an instance of the `AgentInfo` class is held as `self._agent_info`." + +#: ../../source/tutorial/agent/agent_control.md:222 +msgid "自分自身のエンティティIDを取得する" +msgstr "Get your own entity ID" + +#: ../../source/tutorial/agent/agent_control.md:230 +msgid "`Human Detector` モジュールの実装" +msgstr "Implementation of the `Human Detector` Module" + +#: ../../source/tutorial/agent/agent_control.md:232 +msgid "" +"`Human Detector` モジュールの実装を行います。 以下のコードを`fire_brigade_human_detector.py` " +"に記述してください。" +msgstr "" +"Implement the `Human Detector` module. Please write the following code in `fire_brigade_human_detector.py`." + +#: ../../source/tutorial/agent/agent_control.md:348 +msgid "埋没している市民を消防隊エージェントが救出できるようになっていれば成功です。" +msgstr "" +"It is successful if the fire brigade agent can rescue the buried citizens." + +#: ../../source/tutorial/config/config.md:1 +msgid "コンフィグについて" +msgstr "About Config" + +#: ../../source/tutorial/config/config.md:3 +msgid "コンフィグについての説明" +msgstr "Explanation about Config" + +#: ../../source/tutorial/config/config.md:14 +msgid "`config` ディレクトリには、エージェントの設定ファイルが格納されています。" +msgstr "" +"The `config` directory contains the agent's configuration files." + +#: ../../source/tutorial/config/config.md:16 +msgid "launcher.yaml" +msgstr "launcher.yaml" + +#: ../../source/tutorial/config/config.md:18 +msgid "" +"`launcher.yaml` は、エージェントの起動時に読み込まれる設定ファイルです。 " +"シミュレーションサーバーの指定や読み込むモジュールが記述されたファイルの指定などが記述されています。" +msgstr "" +"`launcher.yaml` is a configuration file that is read when the agent starts. " +"It specifies the simulation server and the files of the modules to be loaded." + +#: ../../source/tutorial/config/config.md:21 +msgid "module.yaml" +msgstr "module.yaml" + +#: ../../source/tutorial/config/config.md:23 +msgid "" +"`module.yaml` は、エージェントが読み込むモジュールの設定ファイルです。 読み込むモジュールのパスなどが記述されています。 " +"パスを指定する際は、プロジェクトの`main.py`からの相対パスで指定してください。" +msgstr "" +"`module.yaml` is a configuration file for the modules that the agent loads. It specifies the paths of the modules to be loaded. " +"When specifying the path, use the relative path from the project's `main.py`." + +#: ../../source/tutorial/config/config.md:27 +msgid "例" +msgstr "Example" + +#: ../../source/tutorial/config/config.md:36 +msgid "development.json" +msgstr "development.json" + +#: ../../source/tutorial/config/config.md:38 +msgid "" +"`development.json` は、開発時に使用する設定ファイルです。 エージェントの開発時に外部から値を取得する際に使用します。 " +"そのため、競技時には使用することができません。" +msgstr "" +"`development.json` is a configuration file used during development. It is used to obtain values from external sources during agent development. " +"Therefore, it cannot be used during competitions." + +#: ../../source/tutorial/environment/environment.md:1 +msgid "環境構築" +msgstr "Environment Setup" + +#: ../../source/tutorial/environment/environment.md:2 +msgid "今回はチュートリアル用のシナリオを使用してチュートリアルを行います。" +msgstr "" +"This time, we will use a tutorial scenario for the tutorial." + +#: ../../source/tutorial/environment/environment.md:4 +msgid "チュートリアルで使用するマップのダウンロード" +msgstr "Download the map used in the tutorial" + +#: ../../source/tutorial/environment/environment.md:6 +msgid "" +"{download}`マップのダウンロード <./../../download/tutorial_map.zip>` " +"をクリックしてダウンロードしてください。" +msgstr "" +"Click {download}`Download the map <./../../download/tutorial_map.zip>` to download." + +#: ../../source/tutorial/environment/environment.md:9 +msgid "ダウンロードしたファイルを解凍し、中のファイルを `rcrs-server/maps/` の中に移動させてください。" +msgstr "" +"Unzip the downloaded file and move the files inside to `rcrs-server/maps/`." + +#: ../../source/tutorial/environment/environment.md:11 +msgid "シミュレーションサーバーの動作確認" +msgstr "Check the operation of the simulation server" + +#: ../../source/tutorial/environment/environment.md:18 +msgid "" +"何個かのウィンドウが表示されたら成功です。 コマンドラインで `Ctrl + C` (MacOSの場合は `Command + C` ) " +"を押すとシミュレーションサーバーが終了します。" +msgstr "" +"If several windows are displayed, it is successful. Press `Ctrl + C` (or `Command + C` on MacOS) in the command line to stop the simulation server." + +#: ../../source/tutorial/module/module.md:1 +msgid "モジュールについて" +msgstr "About Modules" + +#: ../../source/tutorial/module/module.md:3 +msgid "モジュールの指定方法" +msgstr "How to specify modules" + +#: ../../source/tutorial/module/module.md:5 +msgid "モジュールを指定するには、module.yamlにモジュールの名前とパスを記述します。" +msgstr "" +"To specify a module, write the module name and path in module.yaml." + +#: ../../source/tutorial/module/module.md:7 +msgid "例えば、以下のように記述します:" +msgstr "For example, write as follows:" + +#: ../../source/tutorial/module/module.md:15 +msgid "" +"この場合、`SampleSearch` というモジュールで使用される、`PathPlanning` と `Clustering` " +"というモジュールを指定しています。" +msgstr "" +"In this case, the modules `PathPlanning` and `Clustering` used in the `SampleSearch` module are specified." + +#: ../../source/tutorial/module/module.md:17 +msgid "モジュールの読み込み方法" +msgstr "How to load modules" + +#: ../../source/tutorial/module/module.md:19 +msgid "モジュール内で、他のモジュールを読み込む場合は、次のように記述します:" +msgstr "" +"To load other modules within a module, write as follows:" + +#: ../../source/tutorial/module/module.md:68 +msgid "モジュールの種類について" +msgstr "About the types of modules" + +#: ../../source/tutorial/module/module.md:70 +msgid "adf-core-pythonでは、以下のモジュールが提供されています。" +msgstr "" +"The following modules are provided in adf-core-python." + +#: ../../source/tutorial/module/module.md +msgid "クラス" +msgstr "Class" + +#: ../../source/tutorial/module/module.md +msgid "役割" +msgstr "Role" + +#: ../../source/tutorial/module/module.md +msgid "TacticsFireBrigade" +msgstr "TacticsFireBrigade" + +#: ../../source/tutorial/module/module.md +msgid "消防隊用のモジュール呼び出し (競技では変更不可)" +msgstr "" +"Module call for fire brigade (cannot be changed in competition)" + +#: ../../source/tutorial/module/module.md +msgid "TacticsPoliceForce" +msgstr "TacticsPoliceForce" + +#: ../../source/tutorial/module/module.md +msgid "土木隊用のモジュール呼び出し (競技では変更不可)" +msgstr "" +"Module call for police force (cannot be changed in competition)" + +#: ../../source/tutorial/module/module.md +msgid "TacticsAmbulanceTeam" +msgstr "TacticsAmbulanceTeam" + +#: ../../source/tutorial/module/module.md +msgid "救急隊用のモジュール呼び出し (競技では変更不可)" +msgstr "" +"Module call for ambulance team (cannot be changed in competition)" + +#: ../../source/tutorial/module/module.md +msgid "BuildingDetector" +msgstr "BuildingDetector" + +#: ../../source/tutorial/module/module.md +msgid "消防隊の活動対象の選択" +msgstr "" +"Selection of activity target for fire brigade" + +#: ../../source/tutorial/module/module.md +msgid "RoadDetector" +msgstr "RoadDetector" + +#: ../../source/tutorial/module/module.md +msgid "土木隊の活動対象の選択" +msgstr "" +"Selection of activity target for police force" + +#: ../../source/tutorial/module/module.md +msgid "HumanDetector" +msgstr "HumanDetector" + +#: ../../source/tutorial/module/module.md +msgid "救急隊の活動対象の選択" +msgstr "" +"Selection of activity target for ambulance team" + +#: ../../source/tutorial/module/module.md +msgid "Search" +msgstr "Search" + +#: ../../source/tutorial/module/module.md +msgid "情報探索に向けた活動対象の選択" +msgstr "" +"Selection of activity target for information search" + +#: ../../source/tutorial/module/module.md +msgid "PathPlanning" +msgstr "PathPlanning" + +#: ../../source/tutorial/module/module.md +msgid "経路探索" +msgstr "Path Planning" + +#: ../../source/tutorial/module/module.md +msgid "DynamicClustering" +msgstr "DynamicClustering" + +#: ../../source/tutorial/module/module.md +msgid "シミュレーションの進行によってクラスタが変化するクラスタリング" +msgstr "" +"Clustering that changes as the simulation progresses" + +#: ../../source/tutorial/module/module.md +msgid "StaticClustering" +msgstr "StaticClustering" + +#: ../../source/tutorial/module/module.md +msgid "シミュレーション開始時にクラスタが定まるクラスタリング" +msgstr "" +"Clustering that is determined at the start of the simulation" + +#: ../../source/tutorial/module/module.md +msgid "ExtAction" +msgstr "ExtAction" + +#: ../../source/tutorial/module/module.md +msgid "活動対象決定後のエージェントの動作決定" +msgstr "" +"Determination of agent actions after selecting the activity target" + +#: ../../source/tutorial/module/module.md +msgid "MessageCoordinator" +msgstr "MessageCoordinator" + +#: ../../source/tutorial/module/module.md +msgid "通信でメッセージ(情報)を送信する経路である、チャンネルへのメッセージの分配" +msgstr "" +"Distribution of messages to channels, which are the routes for sending messages (information) via communication" + +#: ../../source/tutorial/module/module.md +msgid "ChannelSubscriber" +msgstr "ChannelSubscriber" + +#: ../../source/tutorial/module/module.md +msgid "通信によってメッセージを受け取るチャンネルの選択" +msgstr "" +"Selection of channels to receive messages via communication" + +#: ../../source/tutorial/module/module.md:88 +msgid "自分で上記の役割以外のモジュールを作成する際は、`AbstractModule` を継承してください。" +msgstr "" +"When creating a module with a role other than the above, inherit `AbstractModule`." diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 00000000..d0160b5d --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,7 @@ +adf_core_python +=============== + +.. toctree:: + :maxdepth: 4 + + adf_core_python diff --git a/docs/source/quickstart/quickstart.md b/docs/source/quickstart/quickstart.md new file mode 100644 index 00000000..664dcd43 --- /dev/null +++ b/docs/source/quickstart/quickstart.md @@ -0,0 +1,63 @@ +# クイックスタート + +## 前提条件 + +インストールする前に、以下の前提条件を確認してください: + +- Python 3.12 以上 +- pip + +## パッケージのインストール + +パッケージをインストールするには、次のコマンドを実行します: + +```bash +pip install git+https://github.com/adf-python/adf-core-python.git +``` + +## 新規エージェントの作成 + +新規エージェントを作成するには、次のコマンドを実行します: + +```bash +adf-core-python +``` + +実行すると、下記のような対話形式のプロンプトが表示されます: + +```bash +Your agent team name: my-agent +Creating a new agent team with name: my-agent +``` + +入力後、下記のようなエージェントのテンプレートがカレントディレクトリに作成されます。 + +```bash +. +└── my-agent + ├── config + │ ├── development.json + │ ├── launcher.yaml + │ └── module.yaml + ├── main.py + └── src + └── my-agent + ├── __init__.py + └── module + ├── __init__.py + └── complex + ├── __init__.py + ├── sample_human_detector.py + ├── sample_road_detector.py + └── sample_search.py +``` + +## エージェントを実行する + +エージェントを実行するには、シミュレーションサーバーを起動し、次のコマンドを実行します: + +```bash +python main.py +``` + +エージェントの実行が開始され、シミュレーションサーバーとの通信が開始されます。 diff --git a/docs/source/tutorial/agent/agent.md b/docs/source/tutorial/agent/agent.md new file mode 100644 index 00000000..45bab95e --- /dev/null +++ b/docs/source/tutorial/agent/agent.md @@ -0,0 +1,72 @@ +# エージェントの作成 + +このセクションでは、`adf-core-python` ライブラリの使用方法の概要を提供します。 + +## 新規エージェントの作成 + +新規エージェントを作成するには、次のコマンドを実行します: + +```bash +adf-core-python +``` + +実行すると、下記のような対話形式のプロンプトが表示されます: + +```bash +Your agent team name: my-agent +Creating a new agent team with name: my-agent +``` + +```{note} +エージェントチーム名は、エージェントのディレクトリ名として使用されます。 +以後、エージェントチーム名を `` として参照します。 +``` + +入力後、下記のようなエージェントのテンプレートがカレントディレクトリに作成されます。 + +```bash +. +└── my-agent + ├── config + │ ├── development.json + │ ├── launcher.yaml + │ └── module.yaml + ├── main.py + └── src + └── my-agent + ├── __init__.py + └── module + ├── __init__.py + └── complex + ├── __init__.py + ├── sample_human_detector.py + ├── sample_road_detector.py + └── sample_search.py +``` + +## シミュレーションを実行する + +ターミナルを2つ起動します。 + +片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します: + +```bash +# Terminal A +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_fire_brigade_only/map -c ../maps/tutorial_fire_brigade_only/config +``` + +その後、別のターミナルを開き、エージェントを起動します: + +```bash +# Terminal B +cd WORKING_DIR/ +python main.py +``` + +エージェントが正常に起動すると、シミュレーションサーバーに接続され、エージェントがシミュレーションに参加し、エージェントが動き出します。 +途中で止めたい場合は、それぞれのコマンドラインで `Ctrl + C` (MacOSの場合は `Command + C` ) を押してください。 + +```{warning} +シミュレーションサーバーを停止させたあとは、プロセスが残ってしまう場合があるので`./kill.sh` を実行してください。 +``` diff --git a/docs/source/tutorial/agent/agent_control.md b/docs/source/tutorial/agent/agent_control.md new file mode 100644 index 00000000..089fcdce --- /dev/null +++ b/docs/source/tutorial/agent/agent_control.md @@ -0,0 +1,348 @@ +# エージェントの制御 + +このセクションでは、エージェントの制御用のプログラムを作成する方法について説明します。 + +## エージェントの制御について + +RRSの災害救助エージェントは3種類あり、種類毎にそれぞれ異なるプログラムを書く必要があります。しかし、初めから全てのプログラムを書くことは困難です。ここではまず初めに、消防隊エージェントを操作するプログラムの一部を書いてみましょう。 + +```{note} +エージェントを操作するプログラムは、エージェントの種類毎に同一です。 プログラムは各エージェントに配られ、そのエージェントのみの操作を担います。 消防隊エージェントを操作するプログラムを書けば、それがすべての消防隊エージェント上でそれぞれ動作します。 +``` + +![消防隊エージェント](./../../images/agent_control.jpg) + +## エージェントの動作フロー + +エージェントの動作を決めているのはTacticsというプログラムです。 + +消防隊の思考ルーチンは下の図の通りにおおよそおこなわれます。 消防隊の動作としては、まず救助対象の市民を捜索し(`Human Detector`)、見つかった市民を救助します(`Action Rescue`)。 救助対象の市民が見つからない場合は、探索場所を変更して市民を捜索するため、次の捜索場所を決定して(`Search`)移動します(`Action Ext move`)。 なお、エージェントは1ステップ内で、移動と救助活動を同時におこなうことが出来ません。つまり、ステップごとに更新される自身の周辺情報を確認して、動作の対象と動作内容を決定していくということになります。 これらそれぞれの機能が、モジュールと呼ばれるプログラムとして分割して表現されています。 + +今回はこの中で、救助(掘り起こし)対象を決定する `Human Detector` モジュールを開発します。 + +![消防隊エージェントの動作フロー](./../../images/agent_flow.png) + +## `Human Detector` モジュールの実装の準備 + +まず、`Human Detector` モジュールを記述するためのファイルを作成します。 + +```bash +cd WORKING_DIR/ +touch src//module/complex/fire_brigade_human_detector.py +``` + +次に、`Human Detector` モジュールの雛形の実装を行います。 以下のコードを`fire_brigade_human_detector.py` に記述してください。 + +```python +from typing import Optional + +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.logger.logger import get_agent_logger + + +class FireBrigadeHumanDetector(HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + # 計算結果を格納する変数 + self._result: Optional[EntityID] = None + # ロガーの取得 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def calculate(self) -> HumanDetector: + """ + 行動対象を決定する + + Returns + ------- + HumanDetector: 自身のインスタンス + """ + self._logger.info("Calculate FireBrigadeHumanDetector") + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + """ + 行動対象のEntityIDを取得する + + Returns + ------- + Optional[EntityID]: 行動対象のEntityID + """ + return self._result +``` + +## モジュールの登録 + +次に、作成したモジュールを登録します。 + +`WORKING_DIR//config/module.yaml` ファイルを開き、以下の部分を + +```yaml +DefaultTacticsFireBrigade: + HumanDetector: src..module.complex.sample_human_detector.SampleHumanDetector +``` + +以下のように変更してください。 + +```yaml +DefaultTacticsFireBrigade: + HumanDetector: src..module.complex.fire_brigade_human_detector.FireBrigadeHumanDetector +``` + +ターミナルを2つ起動します。 + +片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します: + +```bash +# Terminal A +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_fire_brigade_only/map -c ../maps/tutorial_fire_brigade_only/config +``` + +その後、別のターミナルを開き、エージェントを起動します: + +```bash +# Terminal B +cd WORKING_DIR/ +python main.py +``` + +標準出力に `Calculate FireBrigadeHumanDetector` と表示されれば成功です。 + +## `Human Detector` モジュールの設計 + +救助対象選択プログラムの中身を書き、消防隊エージェントが救助活動をおこなえるように修正します。 + +消防隊エージェントの救助対象を最も簡単に選択する方法は、埋没している市民の中で最も自身に近い市民を選択する方法です。 今回は、この方法を採用した消防隊エージェントの行動対象決定モジュールを書いてみましょう。 + +埋没している市民の中で最も自身に近い市民を救助対象として選択する方法は、フローチャートで表すと下図のようになります。 + +![救助対象選択フローチャート](./../../images/human_detector_flow.png) + +このフローチャート中の各処理を、次小節で紹介する各クラス・メソッド等で置き換えたものを、`fire_brigade_human_detector.py` に記述していくことで、救助対象選択プログラムを完成させます。 + +## `Human Detector` モジュールの実装で使用するクラス・メソッド + +### Entity + +`Entity` クラスは、エンティティの基底クラスです。 このクラスは、エンティティの基本情報を保持します。 + +RRS上のエンティティは下図のように `Entity` を継承したクラスで表現されています。 赤枠で囲まれたクラスは、クラスの意味がそのままRRSの直接的な構成要素を表しています。 + +例: Road クラスのインスタンスの中には、 Hydrant クラスを継承してない通常の道路を表すものも存在しています。 + +![エンティティの継承関係](./../../images/entity.png) + +### EntityID + +`EntityID` クラスは、全てのエージェント/オブジェクトを一意に識別するためのID(識別子)を表すクラスです。 RRSではエージェントとオブジェクトをまとめて、エンティティと呼んでいます。 + +### Civilian + +`Civilian` クラスは、市民を表すクラスです。このクラスからは、エージェントの位置や負傷の進行状況を取得することができます。 + +- `entity` が市民であるかどうかを判定する + +```python +is_civilian: bool = isinstance(entity, Civilian) +``` + +- エンティティIDを取得する + +```python +entity_id: EntityID = entity.get_id() +``` + +- 市民が生きているかどうかを判定する + +```python +hp: Optional[int] = entity.get_hp() +if hp is None or hp <= 0: + return False +``` + +- 市民が埋まっているかどうかを判定する + +```python +buriedness: Optional[int] = entity.get_buriedness() +if buriedness is None or buriedness <= 0: + return False +``` + +### WorldInfo + +`WorldInfo` クラスは、エージェントが把握している情報とそれに関する操作をおこなうメソッドをもったクラスです。 エージェントはこのクラスのインスタンスを通して、他のエージェントやオブジェクトの状態を確認します。 + +モジュール内では、`WorldInfo` クラスのインスタンスを `self._world_info` として保持しています。 + +- エンティティIDからエンティティを取得する + +```python +entity: Entity = self._world_info.get_entity(entity_id) +``` + +- 指定したクラスのエンティティを全て取得する + +```python +entities: list[Entity] = self._world_info.get_entities_by_type([Building, Road]) +``` + +- エージェントの位置から指定したエンティティまでの距離を取得する + +```python +distance: float = self._world_info.get_distance(me, civilian.get_id()) +``` + +[詳細はこちら](../../adf_core_python.core.agent.info.rst) + +### AgentInfo + +`AgentInfo` クラスは、エージェント自身の情報とそれに関する操作をおこなうメソッドをもったクラスです。 エージェントはこのクラスのインスタンスを通して、エージェント自身の状態を取得します。 + +モジュール内では、`AgentInfo` クラスのインスタンスを `self._agent_info` として保持しています。 + +- 自分自身のエンティティIDを取得する + +```python +my_entity_id: EntityID = self._agent_info.get_entity_id() +``` + +[詳細はこちら](../../adf_core_python.core.agent.info.rst) + +## `Human Detector` モジュールの実装 + +`Human Detector` モジュールの実装を行います。 +以下のコードを`fire_brigade_human_detector.py` に記述してください。 + +```python +from typing import Optional + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.logger.logger import get_agent_logger +from rcrs_core.entities.civilian import Civilian +from rcrs_core.entities.entity import Entity +from rcrs_core.worldmodel.entityID import EntityID + + +class FireBrigadeHumanDetector(HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + # 計算結果を格納する変数 + self._result: Optional[EntityID] = None + # ロガーの取得 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def calculate(self) -> HumanDetector: + """ + 行動対象を決定する + + Returns + ------- + HumanDetector: 自身のインスタンス + """ + # 自分自身のEntityIDを取得 + me: EntityID = self._agent_info.get_entity_id() + # すべてのCivilianを取得 + civilians: list[Entity] = self._world_info.get_entities_of_types( + [ + Civilian, + ] + ) + + # 最も近いCivilianを探す + nearest_civilian: Optional[EntityID] = None + nearest_distance: Optional[float] = None + for civilian in civilians: + # civilianがCivilianクラスのインスタンスでない場合はスキップ + if not isinstance(civilian, Civilian): + continue + + # civilianのHPが0以下の場合はすでに死んでしまっているのでスキップ + if civilian.get_hp() <= 0: + continue + + # civilianの埋没度が0以下の場合は掘り起こす必要がないのでスキップ + if civilian.get_buriedness() <= 0: + continue + + # 自分自身との距離を計算 + distance: float = self._world_info.get_distance(me, civilian.get_id()) + + # 最も近いCivilianを更新 + if nearest_distance is None or distance < nearest_distance: + nearest_civilian = civilian.get_id() + nearest_distance = distance + + # 計算結果を格納 + self._result = nearest_civilian + + # ロガーに出力 + self._logger.info(f"Target: {self._result}") + + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + """ + 行動対象のEntityIDを取得する + + Returns + ------- + Optional[EntityID]: 行動対象のEntityID + """ + return self._result +``` + +ターミナルを2つ起動します。 + +片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します: + +```bash +# Terminal A +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_fire_brigade_only/map -c ../maps/tutorial_fire_brigade_only/config +``` + +その後、別のターミナルを開き、エージェントを起動します: + +```bash +# Terminal B +cd WORKING_DIR/ +python main.py +``` + +埋没している市民を消防隊エージェントが救出できるようになっていれば成功です。 diff --git a/docs/source/tutorial/config/config.md b/docs/source/tutorial/config/config.md new file mode 100644 index 00000000..ed552191 --- /dev/null +++ b/docs/source/tutorial/config/config.md @@ -0,0 +1,40 @@ +# コンフィグについて + +## コンフィグについての説明 + +```bash +. +└── + └── config + ├── development.json + ├── launcher.yaml + └── module.yaml +``` + +`config` ディレクトリには、エージェントの設定ファイルが格納されています。 + +### launcher.yaml + +`launcher.yaml` は、エージェントの起動時に読み込まれる設定ファイルです。 +シミュレーションサーバーの指定や読み込むモジュールが記述されたファイルの指定などが記述されています。 + +### module.yaml + +`module.yaml` は、エージェントが読み込むモジュールの設定ファイルです。 +読み込むモジュールのパスなどが記述されています。 +パスを指定する際は、プロジェクトの`main.py`からの相対パスで指定してください。 + +```{admonition} 例 +:class: note + +```yaml +DefaultSearch: + PathPlanning: src..module.complex.sample_search.SampleSearch + Clustering: src..module.complex.sample_search.SampleClustering +``` + +### development.json + +`development.json` は、開発時に使用する設定ファイルです。 +エージェントの開発時に外部から値を取得する際に使用します。 +そのため、競技時には使用することができません。 diff --git a/docs/source/tutorial/environment/environment.md b/docs/source/tutorial/environment/environment.md new file mode 100644 index 00000000..bacef41f --- /dev/null +++ b/docs/source/tutorial/environment/environment.md @@ -0,0 +1,19 @@ +# 環境構築 +今回はチュートリアル用のシナリオを使用してチュートリアルを行います。 + +## チュートリアルで使用するマップのダウンロード + +{download}`マップのダウンロード <./../../download/tutorial_map.zip>` +をクリックしてダウンロードしてください。 + +ダウンロードしたファイルを解凍し、中のファイルを `rcrs-server/maps/` の中に移動させてください。 + +## シミュレーションサーバーの動作確認 + +```bash +cd WORKING_DIR/rcrs-server/scripts +./start-comprun.sh -m ../maps/tutorial_fire_brigade_only/map -c ../maps/tutorial_fire_brigade_only/config +``` + +何個かのウィンドウが表示されたら成功です。 +コマンドラインで `Ctrl + C` (MacOSの場合は `Command + C` ) を押すとシミュレーションサーバーが終了します。 diff --git a/docs/source/tutorial/module/module.md b/docs/source/tutorial/module/module.md new file mode 100644 index 00000000..43f90f24 --- /dev/null +++ b/docs/source/tutorial/module/module.md @@ -0,0 +1,88 @@ +# モジュールについて + +## モジュールの指定方法 + +モジュールを指定するには、module.yamlにモジュールの名前とパスを記述します。 + +例えば、以下のように記述します: + +```yaml +SampleSearch: + PathPlanning: src..module.complex.sample_search.SampleSearch + Clustering: src..module.complex.sample_search.SampleClustering +``` + +この場合、`SampleSearch` というモジュールで使用される、`PathPlanning` と `Clustering` というモジュールを指定しています。 + +## モジュールの読み込み方法 + +モジュール内で、他のモジュールを読み込む場合は、次のように記述します: + +```python +from adf_core_python.core.agent.module.module_manager import ModuleManager + +class SampleSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + # クラスタリングモジュールの取得 + self._clustering: Clustering = cast( + # モジュールの型を指定 + Clustering, + # モジュールの取得 + module_manager.get_module( + # モジュールの名前 + "DefaultSearch.Clustering", + # 上記のモジュールの名前が指定されていない場合のデフォルトのモジュールのパス + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + # パスプランニングモジュールの取得 + self._path_planning: PathPlanning = cast( + # モジュールの型を指定 + PathPlanning, + # モジュールの取得 + module_manager.get_module( + # モジュールの名前 + "DefaultSearch.PathPlanning", + # 上記のモジュールの名前が指定されていない場合のデフォルトのモジュールのパス + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + # モジュールの登録(これをしないと、モジュール内のシミュレーション環境の情報が更新されません) + self.register_sub_module(self._clustering) + self.register_sub_module(self._path_planning) +``` + +## モジュールの種類について + +adf-core-pythonでは、以下のモジュールが提供されています。 + +| クラス | 役割 | +| -------------------- | -------------------------------------------------------------------------- | +| TacticsFireBrigade | 消防隊用のモジュール呼び出し (競技では変更不可) | +| TacticsPoliceForce | 土木隊用のモジュール呼び出し (競技では変更不可) | +| TacticsAmbulanceTeam | 救急隊用のモジュール呼び出し (競技では変更不可) | +| BuildingDetector | 消防隊の活動対象の選択 | +| RoadDetector | 土木隊の活動対象の選択 | +| HumanDetector | 救急隊の活動対象の選択 | +| Search | 情報探索に向けた活動対象の選択 | +| PathPlanning | 経路探索 | +| DynamicClustering | シミュレーションの進行によってクラスタが変化するクラスタリング | +| StaticClustering | シミュレーション開始時にクラスタが定まるクラスタリング | +| ExtAction | 活動対象決定後のエージェントの動作決定 | +| MessageCoordinator | 通信でメッセージ(情報)を送信する経路である、チャンネルへのメッセージの分配 | +| ChannelSubscriber | 通信によってメッセージを受け取るチャンネルの選択 | + +自分で上記の役割以外のモジュールを作成する際は、`AbstractModule` を継承してください。 diff --git a/docs/versions.yaml b/docs/versions.yaml new file mode 100644 index 00000000..f255c463 --- /dev/null +++ b/docs/versions.yaml @@ -0,0 +1,10 @@ +"1.0": + tag: "1.0" + languages: + - "en" + - "ja" +"2.0": + tag: "2.0" + languages: + - "en" + - "ja" diff --git a/java/.gitattributes b/java/.gitattributes new file mode 100644 index 00000000..f91f6460 --- /dev/null +++ b/java/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/java/.gitignore b/java/.gitignore new file mode 100644 index 00000000..4158e079 --- /dev/null +++ b/java/.gitignore @@ -0,0 +1,7 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build + +!lib \ No newline at end of file diff --git a/java/gradle/libs.versions.toml b/java/gradle/libs.versions.toml new file mode 100644 index 00000000..81f63520 --- /dev/null +++ b/java/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.2.1-jre" +junit-jupiter = "5.10.3" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/java/gradle/wrapper/gradle-wrapper.jar b/java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..df97d72b --- /dev/null +++ b/java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/java/gradlew b/java/gradlew new file mode 100755 index 00000000..f5feea6d --- /dev/null +++ b/java/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java/gradlew.bat b/java/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/java/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java/lib/build.gradle b/java/lib/build.gradle new file mode 100644 index 00000000..61869cf9 --- /dev/null +++ b/java/lib/build.gradle @@ -0,0 +1,80 @@ +plugins { + id('java-library') + id('com.google.protobuf') version "0.9.4" + id('maven-publish') +} + +repositories { + mavenCentral() + + maven { + url = 'https://sourceforge.net/projects/jsi/files/m2_repo' + } + maven { + url = 'https://repo.enonic.com/public/' + } + maven { + url 'https://jitpack.io' + } +} + +dependencies { + implementation 'com.github.roborescue:rcrs-server:master-SNAPSHOT' + implementation 'com.github.roborescue:adf-core-java:master-SNAPSHOT' + + // PrecomputeData + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.13.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.1' + implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.0' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.1' + + // Protobuf + implementation 'com.google.protobuf:protobuf-java:3.19.1' + + // Annotation + implementation 'jakarta.annotation:jakarta.annotation-api:3.0.0' + + // Logger + implementation 'org.apache.logging.log4j:log4j-core:2.24.2' + implementation 'org.apache.logging.log4j:log4j-api:2.24.2' + + //Algorithm + implementation 'com.google.common:google-collect:0.5' + + testImplementation libs.junit.jupiter + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.named('test') { + useJUnitPlatform() +} + +publishing { + publications { + mavenJava(MavenPublication) { + groupId = 'com.github.adf-python' + artifactId = 'adf-core-python' + version = 'master-SNAPSHOT' + + from components.java + } + } + + repositories { + maven { + name = "GitHubPackages" + url = "https://maven.pkg.github.com/adf-python/adf-core-python" + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/Main.java b/java/lib/src/main/java/adf_core_python/Main.java new file mode 100644 index 00000000..a85ba196 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/Main.java @@ -0,0 +1,10 @@ +package adf_core_python; + +import adf_core_python.core.gateway.Gateway; + +public class Main { + public static void main(String[] args) { + Gateway gateway = new Gateway(27941); + gateway.start(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/Agent.java b/java/lib/src/main/java/adf_core_python/core/agent/Agent.java new file mode 100644 index 00000000..5524e7d7 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/Agent.java @@ -0,0 +1,173 @@ +package adf_core_python.core.agent; + +import adf.core.agent.communication.standard.bundle.StandardMessageBundle; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.launcher.ConsoleOutput; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.communication.standard.StandardCommunicationModule; +import adf_core_python.core.agent.config.ModuleConfig; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.communication.CommunicationModule; +import adf_core_python.core.component.module.AbstractModule; +import adf_core_python.core.gateway.Coordinator; +import adf_core_python.core.gateway.mapper.AbstractMapper; +import adf_core_python.core.gateway.mapper.MapperDict; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.messages.Message; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Objects; + +public class Agent { + public final AgentInfo agentInfo; + public final WorldInfo worldInfo; + public final ScenarioInfo scenarioInfo; + private final ModuleManager moduleManager; + private final DevelopData developData; + private final PrecomputeData precomputeData; + private final MessageManager messageManager; + private final HashMap modules = new HashMap<>(); + private final MapperDict mapperDict; + private final Logger logger; + private final Coordinator coordinator; + private CommunicationModule communicationModule; + private int ignoreTime; + + public Agent(EntityID entityID, Collection entities, ScenarioInfo scenarioInfo, DevelopData developData, ModuleConfig moduleConfig, Coordinator coordinator) { + StandardWorldModel worldModel = new StandardWorldModel(); + worldModel.addEntities(entities); + worldModel.index(); + + this.ignoreTime = scenarioInfo.getRawConfig() + .getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY); + + this.agentInfo = new AgentInfo(entityID, worldModel); + this.worldInfo = new WorldInfo(worldModel); + this.scenarioInfo = scenarioInfo; + this.developData = developData; + this.moduleManager = new ModuleManager(this.agentInfo, this.worldInfo, this.scenarioInfo, moduleConfig, this.developData); + this.coordinator = coordinator; + + String dataStorageName = ""; + StandardEntityURN agentURN = Objects.requireNonNull(this.worldInfo.getEntity(this.agentInfo.getID())).getStandardURN(); + if (agentURN == StandardEntityURN.AMBULANCE_TEAM || agentURN == StandardEntityURN.AMBULANCE_CENTRE) { + dataStorageName = "ambulance.bin"; + } + if (agentURN == StandardEntityURN.FIRE_BRIGADE || agentURN == StandardEntityURN.FIRE_STATION) { + dataStorageName = "fire.bin"; + } + if (agentURN == StandardEntityURN.POLICE_FORCE || agentURN == StandardEntityURN.POLICE_OFFICE) { + dataStorageName = "police.bin"; + } + + this.precomputeData = new PrecomputeData(dataStorageName); + this.messageManager = new MessageManager(); + + this.mapperDict = new MapperDict(); + + logger = LogManager.getLogger(this.getClass()); + logger.debug("New Agent Created (EntityID: {}, All Entities: {})", this.agentInfo.getID(), this.worldInfo.getAllEntities()); + } + + public Class registerModule(@Nonnull String moduleID, @Nonnull String moduleName, @Nullable String defaultClassName) { + AbstractModule abstractModule = moduleManager.getModule(moduleName, defaultClassName); + if (abstractModule == null) return null; + Class clazz = abstractModule.getClass(); + while (clazz.getSuperclass() != null) { + if (mapperDict.getMapper(clazz) != null) { + Class mapperClass = mapperDict.getMapper(clazz); + try { + Constructor constructor = mapperClass.getConstructor(clazz, precomputeData.getClass(), messageManager.getClass()); + AbstractMapper mapperInstance = constructor.newInstance(abstractModule, precomputeData, messageManager); + modules.put(moduleID, mapperInstance); + logger.debug("Registered Human Detector (ModuleID: {}, Instance: {})", moduleID, modules.get(moduleID)); + return mapperInstance.getTargetClass(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { + throw new RuntimeException(e); + } + } + clazz = clazz.getSuperclass(); + } + return null; + } + + public void update(int time, ChangeSet changed, Collection heard) { + worldInfo.setTime(time); + worldInfo.merge(changed); + agentInfo.recordThinkStartTime(); + agentInfo.setTime(time); + + if (time == 1) { + if (this.communicationModule != null) { + ConsoleOutput.out(ConsoleOutput.State.ERROR, + "[ERROR ] Loader is not found."); + ConsoleOutput.out(ConsoleOutput.State.NOTICE, + "CommunicationModule is modified - " + this); + } else { + this.communicationModule = new StandardCommunicationModule(); + } + + this.messageManager.registerMessageBundle(new StandardMessageBundle()); + } + + // agents can subscribe after ignore time + if (time >= ignoreTime) { + this.messageManager.subscribe(this.agentInfo, this.worldInfo, + this.scenarioInfo); + + if (!this.messageManager.getIsSubscribed()) { + int[] channelsToSubscribe = this.messageManager.getChannels(); + if (channelsToSubscribe != null) { + this.messageManager.setIsSubscribed(true); + } + } + } + + agentInfo.setHeard(heard); + agentInfo.setChanged(changed); + worldInfo.setChanged(changed); + + this.messageManager.refresh(); + this.communicationModule.receive(this, this.messageManager); + + this.messageManager.coordinateMessages(this.agentInfo, this.worldInfo, + this.scenarioInfo); + this.communicationModule.send(this, this.messageManager); + + logger.debug("Agent Update (Time: {}, Changed: {}, Heard: {})", agentInfo.getTime(), agentInfo.getChanged(), agentInfo.getHeard()); + } + + public Config execModuleMethod(String moduleID, String methodName, Config arguments) { + logger.debug("Executing Method (MethodName: {}, Arguments: {}", methodName, arguments); + Config result = modules.get(moduleID).execMethod(methodName, arguments); + logger.debug("Executed Method Result (MethodName: {}, Result: {}", methodName, result); + return result; + } + + public EntityID getID() { + return this.agentInfo.getID(); + } + + public void send(Message[] messages) { + Arrays.stream(messages).forEach(coordinator::sendMessage); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/communication/MessageManager.java b/java/lib/src/main/java/adf_core_python/core/agent/communication/MessageManager.java new file mode 100644 index 00000000..e82e0a5e --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/communication/MessageManager.java @@ -0,0 +1,240 @@ +package adf_core_python.core.agent.communication; + +import adf.core.agent.communication.standard.bundle.StandardMessageBundle; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf.core.component.communication.MessageBundle; +import adf.core.launcher.ConsoleOutput; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.component.communication.ChannelSubscriber; +import adf_core_python.core.component.communication.MessageCoordinator; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +import java.util.*; + +public class MessageManager { + + private final HashMap> messageClassMap; + private final HashMap, + Integer> messageClassIDMap; + private final ArrayList sendMessageList; + private final List receivedMessageList; + private final Set checkDuplicationCache; + private int standardMessageClassCount; + private int customMessageClassCount; + private List> channelSendMessageList; + private int heardAgentHelpCount; + private MessageCoordinator messageCoordinator; + private ChannelSubscriber channelSubscriber; + private int[] subscribedChannels; + private boolean isSubscribed; + + public MessageManager() { + this.standardMessageClassCount = 1; // 00001 + this.customMessageClassCount = 16; // 10000 + this.messageClassMap = new HashMap<>(32); + this.messageClassIDMap = new HashMap<>(32); + this.sendMessageList = new ArrayList<>(); + this.channelSendMessageList = new ArrayList<>(); + this.checkDuplicationCache = new HashSet<>(); + this.receivedMessageList = new ArrayList<>(); + this.heardAgentHelpCount = 0; + + this.messageCoordinator = null; + + channelSubscriber = null; + subscribedChannels = new int[1]; + // by default subscribe to channel 1 + subscribedChannels[0] = 1; + isSubscribed = false; + } + + + public void subscribeToChannels(int[] channels) { + subscribedChannels = channels; + isSubscribed = false; + } + + + public int[] getChannels() { + return subscribedChannels; + } + + + public boolean getIsSubscribed() { + return isSubscribed; + } + + + public void setIsSubscribed(boolean subscribed) { + isSubscribed = subscribed; + } + + + public void setMessageCoordinator(MessageCoordinator mc) { + this.messageCoordinator = mc; + } + + + public void setChannelSubscriber(ChannelSubscriber cs) { + channelSubscriber = cs; + } + + + public void subscribe(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo) { + if (channelSubscriber != null) { + channelSubscriber.subscribe(agentInfo, worldInfo, scenarioInfo, this); + } + } + + + public boolean registerMessageClass(int index, + @Nonnull Class messageClass) { + if (index > 31) { + throw new IllegalArgumentException("index maximum is 31"); + } + + if (messageClassMap.containsKey(index)) { + ConsoleOutput.out(ConsoleOutput.State.WARN, + "index(" + index + ") is already registered/" + messageClass.getName() + + " is ignored"); + return false; + } + + messageClassMap.put(index, messageClass); + messageClassIDMap.put(messageClass, index); + + return true; + } + + + public void registerMessageBundle(@Nonnull MessageBundle messageBundle) { + if (messageBundle == null) { + return; + } + + for (Class messageClass : messageBundle + .getMessageClassList()) { + this.registerMessageClass( + (messageBundle.getClass().equals(StandardMessageBundle.class) + ? standardMessageClassCount++ + : customMessageClassCount++), + messageClass); + } + } + + + @Nullable + public Class getMessageClass(int index) { + if (!messageClassMap.containsKey(index)) { + return null; + } + + return messageClassMap.get(index); + } + + + public int getMessageClassIndex(@Nonnull CommunicationMessage message) { + if (!messageClassMap.containsValue(message.getClass())) { + throw new IllegalArgumentException( + message.getClass().getName() + " is not registered with the manager"); + } + + return messageClassIDMap.get(message.getClass()); + } + + + public void addMessage(@Nonnull CommunicationMessage message) { + this.addMessage(message, true); + } + + + public void addMessage(@Nonnull CommunicationMessage message, + boolean checkDuplication) { + if (message == null) { + return; + } + + String checkKey = message.getCheckKey(); + if (checkDuplication && !this.checkDuplicationCache.contains(checkKey)) { + this.sendMessageList.add(message); + this.checkDuplicationCache.add(checkKey); + } else { + this.sendMessageList.add(message); + this.checkDuplicationCache.add(checkKey); + } + } + + + @Nonnull + public List> getSendMessageList() { + return this.channelSendMessageList; + } + + + public void addReceivedMessage(@Nonnull CommunicationMessage message) { + receivedMessageList.add(message); + } + + + @Nonnull + public List getReceivedMessageList() { + return this.receivedMessageList; + } + + + @SafeVarargs + @Nonnull + public final List getReceivedMessageList( + Class... messageClasses) { + List resultList = new ArrayList<>(); + for (CommunicationMessage message : this.receivedMessageList) { + for (Class< + ? extends CommunicationMessage> messageClass : messageClasses) { + if (messageClass.isAssignableFrom(message.getClass())) { + resultList.add(message); + } + } + } + return resultList; + } + + + public void coordinateMessages(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo) { + // create a list of messages for every channel including the voice comm + // channel + this.channelSendMessageList = new ArrayList>( + scenarioInfo.getCommsChannelsCount()); + for (int i = 0; i < scenarioInfo.getCommsChannelsCount(); i++) { + this.channelSendMessageList.add(new ArrayList()); + } + + if (messageCoordinator != null) { + messageCoordinator.coordinate(agentInfo, worldInfo, scenarioInfo, this, + this.sendMessageList, this.channelSendMessageList); + } + } + + + public void addHeardAgentHelpCount() { + this.heardAgentHelpCount++; + } + + + public int getHeardAgentHelpCount() { + return this.heardAgentHelpCount; + } + + + public void refresh() { + this.sendMessageList.clear(); + this.checkDuplicationCache.clear(); + this.receivedMessageList.clear(); + this.heardAgentHelpCount = 0; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/communication/standard/StandardCommunicationModule.java b/java/lib/src/main/java/adf_core_python/core/agent/communication/standard/StandardCommunicationModule.java new file mode 100644 index 00000000..90bf17db --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/communication/standard/StandardCommunicationModule.java @@ -0,0 +1,175 @@ +package adf_core_python.core.agent.communication.standard; + +import adf.core.agent.communication.standard.bundle.StandardMessage; +import adf.core.component.communication.CommunicationMessage; +import adf.core.component.communication.util.BitOutputStream; +import adf.core.component.communication.util.BitStreamReader; +import adf.core.launcher.ConsoleOutput; +import adf_core_python.core.agent.Agent; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.component.communication.CommunicationModule; +import jakarta.annotation.Nonnull; +import rescuecore2.messages.Command; +import rescuecore2.messages.Message; +import rescuecore2.standard.messages.AKSpeak; +import rescuecore2.worldmodel.EntityID; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +public class StandardCommunicationModule extends CommunicationModule { + + final Class[] standardMessageArgTypes = {boolean.class, int.class, + int.class, BitStreamReader.class}; + final private int ESCAPE_CHAR = 0x41; + final private int SIZE_ID = 5; + final private int SIZE_TTL = 3; + + @Override + public void receive(@Nonnull Agent agent, + @Nonnull MessageManager messageManager) { + Collection heardList = agent.agentInfo.getHeard(); + + for (Command heard : heardList) { + if (heard instanceof AKSpeak) { + EntityID senderID = heard.getAgentID(); + + if (agent.getID().equals(senderID)) { + continue; + } + + AKSpeak received = (AKSpeak) heard; + byte[] receivedData = received.getContent(); + boolean isRadio = (received.getChannel() != 0); + + if (receivedData.length <= 0) { + continue; + } + + if (isRadio) { + addReceivedMessage(messageManager, Boolean.TRUE, senderID, + receivedData); + } else { + String voiceString = new String(receivedData); + if ("Help".equalsIgnoreCase(voiceString) + || "Ouch".equalsIgnoreCase(voiceString)) { + messageManager.addHeardAgentHelpCount(); + continue; + } + + BitOutputStream messageTemp = new BitOutputStream(); + for (int i = 0; i < receivedData.length; i++) { + if (receivedData[i] == ESCAPE_CHAR) { + if ((i + 1) >= receivedData.length) { + addReceivedMessage(messageManager, Boolean.FALSE, senderID, + messageTemp.toByteArray()); + break; + } else if (receivedData[i + 1] != ESCAPE_CHAR) { + addReceivedMessage(messageManager, Boolean.FALSE, senderID, + messageTemp.toByteArray()); + messageTemp.reset(); + continue; + } + + i += 1; + } + messageTemp.write(receivedData[i]); + } + } + } + } + } + + private void addReceivedMessage(@Nonnull MessageManager messageManager, + boolean isRadio, @Nonnull EntityID senderID, byte[] data) { + BitStreamReader bitStreamReader = new BitStreamReader(data); + int messageClassIndex = bitStreamReader.getBits(SIZE_ID); + if (messageClassIndex <= 0) { + ConsoleOutput.out(ConsoleOutput.State.WARN, + "ignore Message Class Index (0)"); + return; + } + + int messageTTL = (isRadio ? -1 : bitStreamReader.getBits(SIZE_TTL)); + + Object[] args = {Boolean.valueOf(isRadio), + Integer.valueOf(senderID.getValue()), Integer.valueOf(messageTTL), + bitStreamReader}; + try { + messageManager + .addReceivedMessage(messageManager.getMessageClass(messageClassIndex) + .getConstructor(standardMessageArgTypes).newInstance(args)); + } catch (NoSuchMethodException | IllegalArgumentException e) { + e.printStackTrace(); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + + @Override + public void send(@Nonnull Agent agent, + @Nonnull MessageManager messageManager) { + final int voiceLimitBytes = agent.scenarioInfo.getVoiceMessagesSize(); + int voiceMessageLeft = voiceLimitBytes; + ByteArrayOutputStream voiceMessageStream = new ByteArrayOutputStream(); + + Message[] messages = new Message[1]; + + List> sendMessageList = messageManager + .getSendMessageList(); + for (int channel = 0; channel < sendMessageList.size(); channel++) { + for (CommunicationMessage message : sendMessageList.get(channel)) { + int messageClassIndex = messageManager.getMessageClassIndex(message); + + BitOutputStream bitOutputStream = new BitOutputStream(); + bitOutputStream.writeBits(messageClassIndex, SIZE_ID); + + if (channel == 0) { + bitOutputStream.writeBits(((StandardMessage) message).getTTL(), + SIZE_TTL); + } + + bitOutputStream.writeBits(message.toBitOutputStream()); + + if (channel > 0) { + messages[0] = new AKSpeak(agent.getID(), agent.agentInfo.getTime(), + channel, bitOutputStream.toByteArray()); + agent.send(messages); + } else { + // voice channel + int messageSize = (int) Math + .ceil(((double) bitOutputStream.size()) / 8.0); + if (messageSize <= voiceMessageLeft) { + byte[] messageData = bitOutputStream.toByteArray(); + ByteArrayOutputStream escapedMessage = new ByteArrayOutputStream(); + for (int i = 0; i < messageSize; i++) { + if (messageData[i] == ESCAPE_CHAR) { + escapedMessage.write(ESCAPE_CHAR); + } + escapedMessage.write(messageData[i]); + } + escapedMessage.toByteArray(); + escapedMessage.write(ESCAPE_CHAR); + if (escapedMessage.size() <= voiceMessageLeft) { + voiceMessageLeft -= escapedMessage.size(); + try { + voiceMessageStream.write(escapedMessage.toByteArray()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + } + + if (voiceMessageStream.size() > 0) { + messages[0] = new AKSpeak(agent.getID(), agent.agentInfo.getTime(), 0, + voiceMessageStream.toByteArray()); + agent.send(messages); + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/config/ModuleConfig.java b/java/lib/src/main/java/adf_core_python/core/agent/config/ModuleConfig.java new file mode 100644 index 00000000..7a6f4dec --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/config/ModuleConfig.java @@ -0,0 +1,39 @@ +package adf_core_python.core.agent.config; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class ModuleConfig { + private static final String DEFAULT_CONFIG_FILE_NAME = "../config/module.yaml"; + private final JsonNode rootNode; + + public ModuleConfig() { + this(DEFAULT_CONFIG_FILE_NAME); + } + + public ModuleConfig(String configFileName) { + try { + String yamlString = Files.readString(Paths.get(configFileName)); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + rootNode = mapper.readTree(yamlString); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String getValue(String moduleName) { + String[] keys = moduleName.split("\\."); + JsonNode moduleNode = rootNode; + for (String key : keys) { + if (moduleNode.has(key)) { + moduleNode = moduleNode.get(key); + } + } + return moduleNode.asText(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/info/AgentInfo.java b/java/lib/src/main/java/adf_core_python/core/agent/info/AgentInfo.java new file mode 100644 index 00000000..0a2e4f42 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/info/AgentInfo.java @@ -0,0 +1,119 @@ +package adf_core_python.core.agent.info; + +import adf.core.agent.action.Action; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import rescuecore2.messages.Command; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class AgentInfo { + private final EntityID entityID; + private final StandardWorldModel worldModel; + private int time; + private ChangeSet changed; + private Collection heard; + private long thinkStartTime; + + private Map actionHistory; + + public AgentInfo(@Nonnull EntityID entityID, @Nonnull StandardWorldModel worldModel) { + this.entityID = entityID; + this.worldModel = worldModel; + this.time = 0; + this.actionHistory = new HashMap<>(); + recordThinkStartTime(); + } + + public int getTime() { + return this.time; + } + + public void setTime(int time) { + this.time = time; + } + + @Nullable + public Collection getHeard() { + return this.heard; + } + + public void setHeard(Collection heard) { + this.heard = heard; + } + + @Nonnull + public EntityID getID() { + return this.entityID; + } + + public StandardEntity me() { + return this.worldModel.getEntity(this.getID()); + } + + public double getX() { + return this.worldModel.getEntity(this.entityID).getLocation(this.worldModel).first(); + } + + public double getY() { + return this.worldModel.getEntity(this.entityID).getLocation(this.worldModel).second(); + } + + public EntityID getPosition() { + StandardEntity entity = this.worldModel.getEntity(this.getID()); + return entity instanceof Human ? ((Human) entity).getPosition() : entity.getID(); + } + + @Nonnull + public Area getPositionArea() { + return (Area) this.worldModel.getEntity(this.getPosition()); + } + + @Nullable + public ChangeSet getChanged() { + return this.changed; + } + + public void setChanged(ChangeSet changed) { + this.changed = changed; + } + + @Nullable + public Human someoneOnBoard() { + + for (StandardEntity next : this.worldModel + .getEntitiesOfType(StandardEntityURN.CIVILIAN)) { + Human human = (Human) next; + if (human.getPosition().equals(this.entityID)) { + return human; + } + } + + return null; + } + + @Nullable + public Action getExecutedAction(int time) { + if (time > 0) + return this.actionHistory.get(time); + return this.actionHistory.get(this.getTime() + time); + } + + + public void setExecutedAction(int time, @Nullable Action action) { + this.actionHistory.put(time > 0 ? time : this.getTime() + time, action); + } + + public void recordThinkStartTime() { + this.thinkStartTime = System.currentTimeMillis(); + } + + public long getThinkTimeMillis() { + return (System.currentTimeMillis() - this.thinkStartTime); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/module/ModuleManager.java b/java/lib/src/main/java/adf_core_python/core/agent/module/ModuleManager.java new file mode 100644 index 00000000..68c72549 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/module/ModuleManager.java @@ -0,0 +1,428 @@ +package adf_core_python.core.agent.module; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.centralized.CommandExecutor; +import adf.core.component.centralized.CommandPicker; +import adf.core.component.communication.ChannelSubscriber; +import adf.core.component.communication.CommunicationMessage; +import adf.core.component.communication.MessageCoordinator; +import adf_core_python.core.agent.config.ModuleConfig; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.AbstractModule; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import rescuecore2.config.NoSuchConfigOptionException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +public class ModuleManager { + private final Logger logger; + private Map moduleMap; + private Map actionMap; + private Map> executorMap; + private Map pickerMap; + private Map channelSubscriberMap; + private Map messageCoordinatorMap; + private AgentInfo agentInfo; + private WorldInfo worldInfo; + private ScenarioInfo scenarioInfo; + private ModuleConfig moduleConfig; + private DevelopData developData; + + public ModuleManager(@Nonnull AgentInfo agentInfo, @Nonnull WorldInfo worldInfo, @Nonnull ScenarioInfo scenarioInfo, @Nonnull ModuleConfig moduleConfig, @Nonnull DevelopData developData) { + this.agentInfo = agentInfo; + this.worldInfo = worldInfo; + this.scenarioInfo = scenarioInfo; + this.moduleConfig = moduleConfig; + this.developData = developData; + this.moduleMap = new HashMap<>(); + this.actionMap = new HashMap<>(); + this.executorMap = new HashMap<>(); + this.pickerMap = new HashMap<>(); + this.channelSubscriberMap = new HashMap<>(1); + this.messageCoordinatorMap = new HashMap<>(1); + + logger = LogManager.getLogger(this.getClass()); + } + + @SuppressWarnings("unchecked") + @Nullable + public final T + getModule(@Nonnull String moduleName, @Nullable String defaultClassName) { + String className = moduleName; + try { + className = this.moduleConfig.getValue(moduleName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class moduleClass; + try { + moduleClass = Class.forName(className); + logger.info(moduleClass.getName()); + } catch (ClassNotFoundException | NullPointerException e) { + logger.warn("Module " + moduleName + " not found. Using default class " + defaultClassName); + className = defaultClassName; + moduleClass = Class.forName(className); + } + + AbstractModule instance = this.moduleMap.get(className); + if (instance != null) { + return (T) instance; + } + + if (AbstractModule.class.isAssignableFrom(moduleClass)) { + instance = this.getModule((Class) moduleClass); + this.moduleMap.put(className, instance); + return (T) instance; + } + + } catch (ClassNotFoundException | NullPointerException e) { + return null; + } + + throw new IllegalArgumentException( + "Module is not found : " + className); + } + + + @Nonnull + public final T + getModule(@Nonnull String moduleName) { + return this.getModule(moduleName, ""); + } + + + @Nonnull + private AbstractModule getModule(@Nonnull Class moduleClass) { + try { + Constructor constructor = moduleClass.getConstructor( + AgentInfo.class, WorldInfo.class, ScenarioInfo.class, + ModuleManager.class, DevelopData.class); + AbstractModule instance = constructor.newInstance(this.agentInfo, + this.worldInfo, this.scenarioInfo, this, this.developData); + this.moduleMap.put(moduleClass.getCanonicalName(), instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @SuppressWarnings("unchecked") + @Nonnull + public final ExtAction getExtAction(String actionName, + String defaultClassName) { + String className = actionName; + try { + className = this.moduleConfig.getValue(actionName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class actionClass; + try { + actionClass = Class.forName(className); + } catch (ClassNotFoundException | NullPointerException e) { + className = defaultClassName; + actionClass = Class.forName(className); + } + + ExtAction instance = this.actionMap.get(className); + if (instance != null) { + return instance; + } + + if (ExtAction.class.isAssignableFrom(actionClass)) { + instance = this.getExtAction((Class) actionClass); + this.actionMap.put(className, instance); + return instance; + } + } catch (ClassNotFoundException | NullPointerException e) { + throw new RuntimeException(e); + } + throw new IllegalArgumentException( + "ExtAction name is not found : " + className); + } + + + @Nonnull + public final ExtAction getExtAction(String actionName) { + return getExtAction(actionName, ""); + } + + + @Nonnull + private ExtAction getExtAction(Class actionClass) { + try { + Constructor constructor = actionClass.getConstructor( + AgentInfo.class, WorldInfo.class, ScenarioInfo.class, + ModuleManager.class, DevelopData.class); + ExtAction instance = constructor.newInstance(this.agentInfo, + this.worldInfo, this.scenarioInfo, this, this.developData); + this.actionMap.put(actionClass.getCanonicalName(), instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @SuppressWarnings("unchecked") + @Nonnull + public final > E + getCommandExecutor(String executorName, String defaultClassName) { + String className = executorName; + try { + className = this.moduleConfig.getValue(executorName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class actionClass; + try { + actionClass = Class.forName(className); + } catch (ClassNotFoundException | NullPointerException e) { + className = defaultClassName; + actionClass = Class.forName(className); + } + + CommandExecutor< + CommunicationMessage> instance = this.executorMap.get(className); + if (instance != null) { + return (E) instance; + } + + if (CommandExecutor.class.isAssignableFrom(actionClass)) { + instance = this.getCommandExecutor( + (Class>) actionClass); + this.executorMap.put(className, instance); + return (E) instance; + } + } catch (ClassNotFoundException | NullPointerException e) { + throw new RuntimeException(e); + } + + throw new IllegalArgumentException( + "CommandExecutor name is not found : " + className); + } + + + @Nonnull + public final > E + getCommandExecutor(String executorName) { + return getCommandExecutor(executorName, ""); + } + + + @SuppressWarnings("unchecked") + @Nonnull + private > E + getCommandExecutor(Class actionClass) { + try { + Constructor constructor = actionClass.getConstructor(AgentInfo.class, + WorldInfo.class, ScenarioInfo.class, ModuleManager.class, + DevelopData.class); + E instance = constructor.newInstance(this.agentInfo, this.worldInfo, + this.scenarioInfo, this, this.developData); + this.executorMap.put(actionClass.getCanonicalName(), + (CommandExecutor) instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @SuppressWarnings("unchecked") + @Nonnull + public final CommandPicker getCommandPicker(String pickerName, + String defaultClassName) { + String className = pickerName; + try { + className = this.moduleConfig.getValue(pickerName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class actionClass; + try { + actionClass = Class.forName(className); + } catch (ClassNotFoundException | NullPointerException e) { + className = defaultClassName; + actionClass = Class.forName(className); + } + + CommandPicker instance = this.pickerMap.get(className); + if (instance != null) { + return instance; + } + + if (CommandPicker.class.isAssignableFrom(actionClass)) { + instance = this.getCommandPicker((Class) actionClass); + this.pickerMap.put(className, instance); + return instance; + } + } catch (ClassNotFoundException | NullPointerException e) { + throw new RuntimeException(e); + } + + throw new IllegalArgumentException( + "CommandExecutor name is not found : " + className); + } + + + @Nonnull + public final CommandPicker getCommandPicker(String pickerName) { + return getCommandPicker(pickerName, ""); + } + + + @Nonnull + private CommandPicker getCommandPicker(Class actionClass) { + try { + Constructor constructor = actionClass.getConstructor( + AgentInfo.class, WorldInfo.class, ScenarioInfo.class, + ModuleManager.class, DevelopData.class); + CommandPicker instance = constructor.newInstance(this.agentInfo, + this.worldInfo, this.scenarioInfo, this, this.developData); + this.pickerMap.put(actionClass.getCanonicalName(), instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @SuppressWarnings("unchecked") + public final ChannelSubscriber getChannelSubscriber(String subscriberName, + String defaultClassName) { + String className = subscriberName; + try { + className = this.moduleConfig.getValue(subscriberName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class actionClass; + try { + actionClass = Class.forName(className); + } catch (ClassNotFoundException | NullPointerException e) { + className = defaultClassName; + actionClass = Class.forName(className); + } + + ChannelSubscriber instance = this.channelSubscriberMap.get(className); + if (instance != null) { + return instance; + } + + if (ChannelSubscriber.class.isAssignableFrom(actionClass)) { + instance = this + .getChannelSubscriber((Class) actionClass); + this.channelSubscriberMap.put(className, instance); + return instance; + } + } catch (ClassNotFoundException | NullPointerException e) { + throw new RuntimeException(e); + } + + throw new IllegalArgumentException( + "channelSubscriber name is not found : " + className); + } + + + public final ChannelSubscriber getChannelSubscriber(String subscriberName) { + return getChannelSubscriber(subscriberName, ""); + } + + + public final ChannelSubscriber + getChannelSubscriber(Class subsClass) { + try { + Constructor constructor = subsClass.getConstructor(); + ChannelSubscriber instance = constructor.newInstance(); + this.channelSubscriberMap.put(subsClass.getCanonicalName(), instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @SuppressWarnings("unchecked") + public final MessageCoordinator getMessageCoordinator(String coordinatorName, + String defaultClassName) { + String className = coordinatorName; + try { + className = this.moduleConfig.getValue(coordinatorName); + } catch (NoSuchConfigOptionException ignored) { + } + + try { + Class actionClass; + try { + actionClass = Class.forName(className); + } catch (ClassNotFoundException | NullPointerException e) { + className = defaultClassName; + actionClass = Class.forName(className); + } + + MessageCoordinator instance = this.messageCoordinatorMap.get(className); + if (instance != null) { + return instance; + } + + if (MessageCoordinator.class.isAssignableFrom(actionClass)) { + instance = this + .getMessageCoordinator((Class) actionClass); + this.messageCoordinatorMap.put(className, instance); + return instance; + } + } catch (ClassNotFoundException | NullPointerException e) { + throw new RuntimeException(e); + } + + throw new IllegalArgumentException( + "channelSubscriber name is not found : " + className); + } + + + public final MessageCoordinator + getMessageCoordinator(String coordinatorName) { + return getMessageCoordinator(coordinatorName, ""); + } + + + public final MessageCoordinator + getMessageCoordinator(Class subsClass) { + try { + Constructor constructor = subsClass.getConstructor(); + MessageCoordinator instance = constructor.newInstance(); + this.messageCoordinatorMap.put(subsClass.getCanonicalName(), instance); + return instance; + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + + @Nonnull + public ModuleConfig getModuleConfig() { + return this.moduleConfig; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/precompute/PreData.java b/java/lib/src/main/java/adf_core_python/core/agent/precompute/PreData.java new file mode 100644 index 00000000..aea850f2 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/precompute/PreData.java @@ -0,0 +1,56 @@ +package adf_core_python.core.agent.precompute; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class PreData { + + public Map intValues; + public Map doubleValues; + public Map stringValues; + public Map idValues; + public Map boolValues; + + public Map> intLists; + public Map> doubleLists; + public Map> stringLists; + public Map> idLists; + public Map> boolLists; + + public boolean isReady; + public String readyID; + + public PreData() { + this.intValues = new HashMap<>(); + this.doubleValues = new HashMap<>(); + this.stringValues = new HashMap<>(); + this.idValues = new HashMap<>(); + this.boolValues = new HashMap<>(); + this.intLists = new HashMap<>(); + this.doubleLists = new HashMap<>(); + this.stringLists = new HashMap<>(); + this.idLists = new HashMap<>(); + this.boolLists = new HashMap<>(); + this.isReady = false; + this.readyID = ""; + } + + + public PreData copy() { + PreData preData = new PreData(); + preData.intValues = new HashMap<>(this.intValues); + preData.doubleValues = new HashMap<>(this.doubleValues); + preData.stringValues = new HashMap<>(this.stringValues); + preData.idValues = new HashMap<>(this.idValues); + preData.boolValues = new HashMap<>(this.boolValues); + preData.intLists = new HashMap<>(this.intLists); + preData.doubleLists = new HashMap<>(this.doubleLists); + preData.stringLists = new HashMap<>(this.stringLists); + preData.idLists = new HashMap<>(this.idLists); + preData.boolLists = new HashMap<>(this.boolLists); + preData.isReady = this.isReady; + preData.readyID = this.readyID; + return preData; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/agent/precompute/PrecomputeData.java b/java/lib/src/main/java/adf_core_python/core/agent/precompute/PrecomputeData.java new file mode 100644 index 00000000..583d4013 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/agent/precompute/PrecomputeData.java @@ -0,0 +1,400 @@ +package adf_core_python.core.agent.precompute; + +import adf.core.agent.info.WorldInfo; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.msgpack.jackson.dataformat.MessagePackFactory; +import rescuecore2.worldmodel.EntityID; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class PrecomputeData { + + public static final String DEFAULT_FILE_NAME = "data.bin"; + public static final File PRECOMP_DATA_DIR = new File("precomp_data"); + + private final String fileName; + + private PreData data; + + public PrecomputeData() { + this(DEFAULT_FILE_NAME); + } + + + public PrecomputeData(String name) { + this.fileName = name; + this.init(); + } + + + private PrecomputeData(String name, PreData precomputeDatas) { + this.fileName = name; + this.data = precomputeDatas; + } + + + public static void removeData(String name) { + if (!PRECOMP_DATA_DIR.exists()) { + return; + } + + File file = new File(PRECOMP_DATA_DIR, name); + if (!file.exists()) { + return; + } + + file.delete(); + } + + + public static void removeData() { + removeData(DEFAULT_FILE_NAME); + } + + + public PrecomputeData copy() { + return new PrecomputeData(this.fileName, this.data.copy()); + } + + + private void init() { + this.data = this.read(this.fileName); + if (this.data == null) { + this.data = new PreData(); + } + } + + + private PreData read(String name) { + try { + if (!PRECOMP_DATA_DIR.exists()) { + if (!PRECOMP_DATA_DIR.mkdir()) { + return null; + } + } + + File readFile = new File(PRECOMP_DATA_DIR, name); + if (!readFile.exists()) { + return null; + } + + FileInputStream fis = new FileInputStream(readFile); + BufferedInputStream bis = new BufferedInputStream(fis); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] binary = new byte[1024]; + while (true) { + int len = bis.read(binary); + if (len < 0) { + break; + } + bout.write(binary, 0, len); + } + + binary = bout.toByteArray(); + ObjectMapper om = new ObjectMapper(new MessagePackFactory()); + PreData ds = om.readValue(binary, PreData.class); + bis.close(); + fis.close(); + return ds; + } catch (IOException e) { + return null; + } + } + + + public boolean write() { + try { + if (!PRECOMP_DATA_DIR.exists()) { + if (!PRECOMP_DATA_DIR.mkdir()) { + return false; + } + } + ObjectMapper om = new ObjectMapper(new MessagePackFactory()); + byte[] binary = om.writeValueAsBytes(this.data); + FileOutputStream fos = new FileOutputStream( + new File(PRECOMP_DATA_DIR, this.fileName)); + fos.write(binary); + fos.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + ; + return false; + } + } + + + public Integer setInteger(String name, int value) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.intValues.put(callClassName + ":" + name, value); + } + + + public Double setDouble(String name, double value) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.doubleValues.put(callClassName + ":" + name, value); + } + + + public Boolean setBoolean(String name, boolean value) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.boolValues.put(callClassName + ":" + name, value); + } + + + public String setString(String name, String value) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.stringValues.put(callClassName + ":" + name, value); + } + + + public EntityID setEntityID(String name, EntityID value) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + Integer id = this.data.idValues.put(callClassName + ":" + name, + value.getValue()); + return id == null ? null : new EntityID(id); + } + + + public List setIntegerList(String name, List list) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.intLists.put(callClassName + ":" + name, list); + } + + + public List setDoubleList(String name, List list) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.doubleLists.put(callClassName + ":" + name, list); + } + + + public List setStringList(String name, List list) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.stringLists.put(callClassName + ":" + name, list); + } + + + public List setEntityIDList(String name, List list) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + List cvtList = new ArrayList<>(); + for (EntityID id : list) { + cvtList.add(id.getValue()); + } + + cvtList = this.data.idLists.put(callClassName + ":" + name, cvtList); + return cvtList == null ? null + : cvtList.stream().map(EntityID::new).collect(Collectors.toList()); + } + + + public List setBooleanList(String name, List list) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.boolLists.put(callClassName + ":" + name, list); + } + + + public boolean setReady(boolean isReady, WorldInfo worldInfo) { + this.data.isReady = isReady; + this.data.readyID = makeReadyID(worldInfo); + return (this.data.isReady + && this.data.readyID.equals(this.makeReadyID(worldInfo))); + } + + + public Integer getInteger(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.intValues.get(callClassName + ":" + name); + } + + + public Double getDouble(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.doubleValues.get(callClassName + ":" + name); + } + + + public Boolean getBoolean(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.boolValues.get(callClassName + ":" + name); + } + + + public String getString(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.stringValues.get(callClassName + ":" + name); + } + + + public EntityID getEntityID(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + Integer id = this.data.idValues.get(callClassName + ":" + name); + return id == null ? null : new EntityID(id); + } + + + public List getIntegerList(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.intLists.get(callClassName + ":" + name); + } + + + public List getDoubleList(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.doubleLists.get(callClassName + ":" + name); + } + + + public List getStringList(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.stringLists.get(callClassName + ":" + name); + } + + + public List getEntityIDList(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + List cvtList = this.data.idLists.get(callClassName + ":" + name); + return cvtList == null ? null + : cvtList.stream().map(EntityID::new).collect(Collectors.toList()); + } + + + public List getBooleanList(String name) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + if (stackTraceElements.length == 0) { + return null; + } + + String callClassName = stackTraceElements[2].getClassName(); + return this.data.boolLists.get(callClassName + ":" + name); + } + + + public boolean isReady(WorldInfo worldInfo) { + return (this.data.isReady + && this.data.readyID.equals(this.makeReadyID(worldInfo))); + } + + + private String makeReadyID(WorldInfo worldInfo) { + return "" + worldInfo.getBounds().getX() + worldInfo.getBounds().getY() + + worldInfo.getAllEntities().size(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/communication/ChannelSubscriber.java b/java/lib/src/main/java/adf_core_python/core/component/communication/ChannelSubscriber.java new file mode 100644 index 00000000..5a017b45 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/communication/ChannelSubscriber.java @@ -0,0 +1,19 @@ +package adf_core_python.core.component.communication; + +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; + +public class ChannelSubscriber { + + public void subscribe(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo, MessageManager messageManager) { + // default channel subscriber subscribes to only channel 1 + if (agentInfo.getTime() == 1) { + int[] channels = new int[1]; + channels[0] = 1; + messageManager.subscribeToChannels(channels); + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/communication/CommunicationModule.java b/java/lib/src/main/java/adf_core_python/core/component/communication/CommunicationModule.java new file mode 100644 index 00000000..b5135a1d --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/communication/CommunicationModule.java @@ -0,0 +1,11 @@ +package adf_core_python.core.component.communication; + +import adf_core_python.core.agent.Agent; +import adf_core_python.core.agent.communication.MessageManager; + +abstract public class CommunicationModule { + + abstract public void receive(Agent agent, MessageManager messageManager); + + abstract public void send(Agent agent, MessageManager messageManager); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/communication/MessageCoordinator.java b/java/lib/src/main/java/adf_core_python/core/component/communication/MessageCoordinator.java new file mode 100644 index 00000000..4ebaa2a7 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/communication/MessageCoordinator.java @@ -0,0 +1,18 @@ +package adf_core_python.core.component.communication; + +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; + +import java.util.ArrayList; +import java.util.List; + +abstract public class MessageCoordinator { + + abstract public void coordinate(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo, MessageManager messageManager, + ArrayList sendMessageList, + List> channelSendMessageList); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/extaction/ExtAction.java b/java/lib/src/main/java/adf_core_python/core/component/extaction/ExtAction.java new file mode 100644 index 00000000..f7ba6888 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/extaction/ExtAction.java @@ -0,0 +1,137 @@ +package adf_core_python.core.component.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.worldmodel.EntityID; + +abstract public class ExtAction { + + protected ScenarioInfo scenarioInfo; + protected AgentInfo agentInfo; + protected WorldInfo worldInfo; + protected ModuleManager moduleManager; + protected DevelopData developData; + protected Action result; + private int countPrecompute; + private int countResume; + private int countPreparate; + private int countUpdateInfo; + private int countUpdateInfoCurrentTime; + + public ExtAction(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + this.worldInfo = wi; + this.agentInfo = ai; + this.scenarioInfo = si; + this.moduleManager = moduleManager; + this.developData = developData; + this.result = null; + this.countPrecompute = 0; + this.countResume = 0; + this.countPreparate = 0; + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = 0; + } + + + public abstract ExtAction setTarget(EntityID targets); + + + /** + * @param targets target + * @return ExtAction + * @deprecated {@link #setTarget(EntityID)} + */ + @Deprecated + public ExtAction setTarget(EntityID... targets) { + if (targets != null && targets.length > 0) { + return this.setTarget(targets[0]); + } + return this; + } + + + public abstract ExtAction calc(); + + + public Action getAction() { + return result; + } + + + public ExtAction precompute(PrecomputeData precomputeData) { + this.countPrecompute++; + return this; + } + + + public ExtAction resume(PrecomputeData precomputeData) { + this.countResume++; + return this; + } + + + public ExtAction preparate() { + this.countPreparate++; + return this; + } + + + public ExtAction updateInfo(MessageManager messageManager) { + if (this.countUpdateInfoCurrentTime != this.agentInfo.getTime()) { + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = this.agentInfo.getTime(); + } + this.countUpdateInfo++; + return this; + } + + + public int getCountPrecompute() { + return this.countPrecompute; + } + + + public int getCountResume() { + return this.countResume; + } + + + public int getCountPreparate() { + return this.countPreparate; + } + + + public int getCountUpdateInfo() { + if (this.countUpdateInfoCurrentTime != this.agentInfo.getTime()) { + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = this.agentInfo.getTime(); + } + return this.countUpdateInfo; + } + + + public void resetCountPrecompute() { + this.countPrecompute = 0; + } + + + public void resetCountResume() { + this.countResume = 0; + } + + + public void resetCountPreparate() { + this.countPreparate = 0; + } + + + public void resetCountUpdateInfo() { + this.countUpdateInfo = 0; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/AbstractModule.java b/java/lib/src/main/java/adf_core_python/core/component/module/AbstractModule.java new file mode 100644 index 00000000..e6e6f655 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/AbstractModule.java @@ -0,0 +1,138 @@ +package adf_core_python.core.component.module; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractModule { + + private final List subModules = new ArrayList<>(); + protected AgentInfo agentInfo; + protected WorldInfo worldInfo; + protected ScenarioInfo scenarioInfo; + protected ModuleManager moduleManager; + protected DevelopData developData; + + private int countPrecompute; + private int countResume; + private int countPreparate; + private int countUpdateInfo; + private int countUpdateInfoCurrentTime; + + public AbstractModule(AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, ModuleManager moduleManager, DevelopData developData) { + this.agentInfo = agentInfo; + this.worldInfo = worldInfo; + this.scenarioInfo = scenarioInfo; + this.moduleManager = moduleManager; + this.developData = developData; + this.countPrecompute = 0; + this.countResume = 0; + this.countPreparate = 0; + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = 0; + } + + + protected void registerModule(AbstractModule module) { + subModules.add(module); + } + + + protected boolean unregisterModule(AbstractModule module) { + return subModules.remove(module); + } + + + public AbstractModule precompute(PrecomputeData precomputeData) { + this.countPrecompute++; + for (AbstractModule abstractModule : subModules) { + abstractModule.precompute(precomputeData); + } + return this; + } + + + public AbstractModule resume(PrecomputeData precomputeData) { + this.countResume++; + for (AbstractModule abstractModule : subModules) { + abstractModule.resume(precomputeData); + } + return this; + } + + + public AbstractModule preparate() { + this.countPreparate++; + for (AbstractModule abstractModule : subModules) { + abstractModule.preparate(); + } + return this; + } + + + public AbstractModule updateInfo(MessageManager messageManager) { + if (this.countUpdateInfoCurrentTime != this.agentInfo.getTime()) { + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = this.agentInfo.getTime(); + } + for (AbstractModule abstractModule : subModules) { + abstractModule.updateInfo(messageManager); + } + this.countUpdateInfo++; + return this; + } + + + public abstract AbstractModule calc(); + + + public int getCountPrecompute() { + return this.countPrecompute; + } + + + public int getCountResume() { + return this.countResume; + } + + + public int getCountPreparate() { + return this.countPreparate; + } + + + public int getCountUpdateInfo() { + if (this.countUpdateInfoCurrentTime != this.agentInfo.getTime()) { + this.countUpdateInfo = 0; + this.countUpdateInfoCurrentTime = this.agentInfo.getTime(); + } + return this.countUpdateInfo; + } + + + public void resetCountPrecompute() { + this.countPrecompute = 0; + } + + + public void resetCountResume() { + this.countResume = 0; + } + + + public void resetCountPreparate() { + this.countPreparate = 0; + } + + + public void resetCountUpdateInfo() { + this.countUpdateInfo = 0; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/Clustering.java b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/Clustering.java new file mode 100644 index 00000000..1c84cdb4 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/Clustering.java @@ -0,0 +1,86 @@ +package adf_core_python.core.component.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.AbstractModule; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public abstract class Clustering extends AbstractModule { + + public Clustering(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + public abstract int getClusterNumber(); + + public abstract int getClusterIndex(StandardEntity entity); + + public abstract int getClusterIndex(EntityID id); + + public abstract Collection getClusterEntities(int index); + + public abstract Collection getClusterEntityIDs(int index); + + + public List> getAllClusterEntities() { + int number = this.getClusterNumber(); + List> result = new ArrayList<>(number); + for (int i = 0; i < number; i++) { + result.add(i, this.getClusterEntities(i)); + } + return result; + } + + + public List> getAllClusterEntityIDs() { + int number = this.getClusterNumber(); + List> result = new ArrayList<>(number); + for (int i = 0; i < number; i++) { + result.add(i, this.getClusterEntityIDs(i)); + } + return result; + } + + + @Override + public Clustering precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public Clustering resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public Clustering preparate() { + super.preparate(); + return this; + } + + + @Override + public Clustering updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract Clustering calc(); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/DynamicClustering.java b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/DynamicClustering.java new file mode 100644 index 00000000..16d302ac --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/DynamicClustering.java @@ -0,0 +1,14 @@ +package adf_core_python.core.component.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; + +public abstract class DynamicClustering extends Clustering { + + public DynamicClustering(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/PathPlanning.java b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/PathPlanning.java new file mode 100644 index 00000000..7a40f63c --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/PathPlanning.java @@ -0,0 +1,101 @@ +package adf_core_python.core.component.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.AbstractModule; +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.EntityID; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public abstract class PathPlanning extends AbstractModule { + + public PathPlanning(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + public abstract List getResult(); + + public abstract PathPlanning setFrom(EntityID id); + + public abstract PathPlanning setDestination(Collection targets); + + + public PathPlanning setDestination(EntityID... targets) { + return this.setDestination(Arrays.asList(targets)); + } + + + @Override + public PathPlanning precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public PathPlanning resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public PathPlanning preparate() { + super.preparate(); + return this; + } + + + @Override + public PathPlanning updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract PathPlanning calc(); + + + public double getDistance() { + double sum = 0.0; + List path = getResult(); + if (path == null || path.size() <= 1) { + return sum; + } + + Pair prevPoint = null; + for (EntityID id : path) { + Pair point = worldInfo.getLocation(worldInfo.getEntity(id)); + if (prevPoint != null) { + int x = prevPoint.first() - point.first(); + int y = prevPoint.second() - point.second(); + sum += x * x + y * y; + } + prevPoint = point; + } + + return Math.sqrt(sum); + } + + + // Alias + public double getDistance(EntityID from, EntityID dest) { + return this.setFrom(from).setDestination(dest).calc().getDistance(); + } + + + public List getResult(EntityID from, EntityID dest) { + return this.setFrom(from).setDestination(dest).calc().getResult(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/StaticClustering.java b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/StaticClustering.java new file mode 100644 index 00000000..f3fb404e --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/algorithm/StaticClustering.java @@ -0,0 +1,14 @@ +package adf_core_python.core.component.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; + +public abstract class StaticClustering extends Clustering { + + public StaticClustering(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/AmbulanceTargetAllocator.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/AmbulanceTargetAllocator.java new file mode 100644 index 00000000..4ebfb1d6 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/AmbulanceTargetAllocator.java @@ -0,0 +1,47 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.worldmodel.EntityID; + +import java.util.Map; + +public abstract class AmbulanceTargetAllocator extends TargetAllocator { + + public AmbulanceTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public abstract Map getResult(); + + @Override + public abstract AmbulanceTargetAllocator calc(); + + + @Override + public AmbulanceTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public AmbulanceTargetAllocator preparate() { + super.preparate(); + return this; + } + + + @Override + public AmbulanceTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/BuildingDetector.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/BuildingDetector.java new file mode 100644 index 00000000..cc22e349 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/BuildingDetector.java @@ -0,0 +1,49 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.standard.entities.Building; + +public abstract class BuildingDetector extends TargetDetector { + + public BuildingDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public BuildingDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public BuildingDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public BuildingDetector preparate() { + super.preparate(); + return this; + } + + + @Override + public BuildingDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract BuildingDetector calc(); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/FireTargetAllocator.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/FireTargetAllocator.java new file mode 100644 index 00000000..f6791d6a --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/FireTargetAllocator.java @@ -0,0 +1,47 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.worldmodel.EntityID; + +import java.util.Map; + +public abstract class FireTargetAllocator extends TargetAllocator { + + public FireTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public abstract Map getResult(); + + @Override + public abstract FireTargetAllocator calc(); + + + @Override + public FireTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public FireTargetAllocator preparate() { + super.preparate(); + return this; + } + + + @Override + public FireTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/HumanDetector.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/HumanDetector.java new file mode 100644 index 00000000..f0491ffa --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/HumanDetector.java @@ -0,0 +1,49 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.standard.entities.Human; + +public abstract class HumanDetector extends TargetDetector { + + public HumanDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public HumanDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public HumanDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public HumanDetector preparate() { + super.preparate(); + return this; + } + + + @Override + public HumanDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract HumanDetector calc(); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/PoliceTargetAllocator.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/PoliceTargetAllocator.java new file mode 100644 index 00000000..43008955 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/PoliceTargetAllocator.java @@ -0,0 +1,47 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.worldmodel.EntityID; + +import java.util.Map; + +public abstract class PoliceTargetAllocator extends TargetAllocator { + + public PoliceTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public abstract Map getResult(); + + @Override + public abstract PoliceTargetAllocator calc(); + + + @Override + public PoliceTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public PoliceTargetAllocator preparate() { + super.preparate(); + return this; + } + + + @Override + public PoliceTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/RoadDetector.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/RoadDetector.java new file mode 100644 index 00000000..532b8533 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/RoadDetector.java @@ -0,0 +1,49 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.standard.entities.Road; + +public abstract class RoadDetector extends TargetDetector { + + public RoadDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public RoadDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public RoadDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public RoadDetector preparate() { + super.preparate(); + return this; + } + + + @Override + public RoadDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract RoadDetector calc(); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/Search.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/Search.java new file mode 100644 index 00000000..77fbe62d --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/Search.java @@ -0,0 +1,49 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import rescuecore2.standard.entities.Area; + +public abstract class Search extends TargetDetector { + + public Search(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + @Override + public Search precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public Search resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public Search preparate() { + super.preparate(); + return this; + } + + + @Override + public Search updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public abstract Search calc(); +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetAllocator.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetAllocator.java new file mode 100644 index 00000000..f0b93abe --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetAllocator.java @@ -0,0 +1,54 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.AbstractModule; +import rescuecore2.worldmodel.EntityID; + +import java.util.Map; + +public abstract class TargetAllocator extends AbstractModule { + + public TargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + public abstract Map getResult(); + + @Override + public abstract TargetAllocator calc(); + + + @Override + public final TargetAllocator precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public TargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public TargetAllocator preparate() { + super.preparate(); + return this; + } + + + @Override + public TargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetDetector.java b/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetDetector.java new file mode 100644 index 00000000..d0bc184f --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/component/module/complex/TargetDetector.java @@ -0,0 +1,54 @@ +package adf_core_python.core.component.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.AbstractModule; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.worldmodel.EntityID; + +public abstract class TargetDetector + extends AbstractModule { + + public TargetDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + } + + + public abstract EntityID getTarget(); + + @Override + public abstract TargetDetector calc(); + + + @Override + public TargetDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public TargetDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public TargetDetector preparate() { + super.preparate(); + return this; + } + + + @Override + public TargetDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/Coordinator.java b/java/lib/src/main/java/adf_core_python/core/gateway/Coordinator.java new file mode 100644 index 00000000..f056e941 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/Coordinator.java @@ -0,0 +1,138 @@ +package adf_core_python.core.gateway; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.launcher.ConfigKey; +import adf_core_python.core.agent.Agent; +import adf_core_python.core.agent.config.ModuleConfig; +import adf_core_python.core.gateway.message.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.misc.EncodingTools; +import rescuecore2.worldmodel.ChangeSet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Collection; +import java.util.HashMap; + +public class Coordinator extends Thread { + private final InputStream inputStream; + private final OutputStream outputStream; + private final HashMap changeSetTemp = new HashMap<>(); + private final HashMap> heardTemp = new HashMap<>(); + private boolean running = true; + private Agent agent; + + public Coordinator(Socket socket) { + try { + socket.setSoTimeout(1000); + socket.setReuseAddress(true); + inputStream = socket.getInputStream(); + outputStream = socket.getOutputStream(); + } catch (IOException e) { + throw new RuntimeException(e); + } + Logger logger = LogManager.getLogger(this.getClass()); + logger.info("Connected from {} on {}", socket.getRemoteSocketAddress(), socket.getLocalSocketAddress()); + } + + @Override + public void run() { + while (running) { + RCRSProto.MessageProto messageProto = receiveMessage(); + if (messageProto == null) continue; + try { + Message message = ModuleControlMessageFactory.makeMessage(messageProto.getUrn(), messageProto); + handleMessage(message); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private void handleMessage(Message message) { + if (message instanceof AMAgent amAgent) { + ScenarioInfo.Mode mode = ScenarioInfo.Mode.NON_PRECOMPUTE; + switch (amAgent.getMode()) { + case 1: + mode = ScenarioInfo.Mode.PRECOMPUTED; + break; + case 2: + mode = ScenarioInfo.Mode.PRECOMPUTATION_PHASE; + break; + } + Config config = amAgent.getConfig(); + agent = new Agent(amAgent.getAgentID(), amAgent.getEntities(), new ScenarioInfo(config, mode), new DevelopData( + config.getBooleanValue(ConfigKey.KEY_DEVELOP_FLAG, false), + config.getValue(ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, + DevelopData.DEFAULT_FILE_NAME), + config.getArrayValue(ConfigKey.KEY_DEVELOP_DATA, "")), new ModuleConfig(), this); + } else if (message instanceof AMModule amModule) { + if (agent == null) { + throw new IllegalStateException("Agent not found. Make sure agent has been registered."); + } + Class clazz = agent.registerModule(amModule.getModuleID(), amModule.getModuleName(), amModule.getDefaultClassName()); + String class_name = ""; + if (clazz != null) { + class_name = clazz.getName(); + } + MAModuleResponse maModuleResponse = new MAModuleResponse(amModule.getModuleID(), class_name); + sendMessage(maModuleResponse); + } else if (message instanceof AMUpdate amUpdate) { + if (agent == null) { + changeSetTemp.put(amUpdate.getTime(), amUpdate.getChanged()); + heardTemp.put(amUpdate.getTime(), amUpdate.getHeard()); + return; + } + if (!changeSetTemp.isEmpty() && !heardTemp.isEmpty()) { + for (int i = 1; i < amUpdate.getTime(); i++) { + agent.update(i, changeSetTemp.get(i), heardTemp.get(i)); + } + changeSetTemp.clear(); + heardTemp.clear(); + } + agent.update(amUpdate.getTime(), amUpdate.getChanged(), amUpdate.getHeard()); + } else if (message instanceof AMExec amExec) { + Config result = agent.execModuleMethod(amExec.getModuleID(), amExec.getMethodName(), amExec.getArguments()); + MAExecResponse maExecResponse = new MAExecResponse(amExec.getModuleID(), result); + sendMessage(maExecResponse); + } + } + + private MessageProto receiveMessage() { + try { + int size = EncodingTools.readInt32(inputStream); + byte[] bytes = inputStream.readNBytes(size); + return MessageProto.parseFrom(bytes); + } catch (IOException ignored) { + } + return null; + } + + private void sendMessage(MessageProto messageProto) { + try { + byte[] bytes = messageProto.toByteArray(); + EncodingTools.writeInt32(bytes.length, outputStream); + outputStream.write(bytes); + outputStream.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void sendMessage(Message message) { + sendMessage(message.toMessageProto()); + } + + public void shutdown() { + running = false; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/Gateway.java b/java/lib/src/main/java/adf_core_python/core/gateway/Gateway.java new file mode 100644 index 00000000..dc5c4c54 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/Gateway.java @@ -0,0 +1,48 @@ +package adf_core_python.core.gateway; + +import rescuecore2.registry.Registry; +import rescuecore2.standard.entities.StandardEntityFactory; +import rescuecore2.standard.entities.StandardPropertyFactory; +import rescuecore2.standard.messages.StandardMessageFactory; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; + +public class Gateway extends Thread { + private final int port; + private final ArrayList coordinators; + private boolean running = true; + + public Gateway(int port) { + this.port = port; + this.coordinators = new ArrayList<>(); + + Registry.SYSTEM_REGISTRY.registerFactory(StandardEntityFactory.INSTANCE); + Registry.SYSTEM_REGISTRY.registerFactory(StandardMessageFactory.INSTANCE); + Registry.SYSTEM_REGISTRY.registerFactory(StandardPropertyFactory.INSTANCE); + } + + @Override + public void run() { + try (ServerSocket serverSocket = new ServerSocket(port)) { + serverSocket.setReuseAddress(true); + while (running) { + Socket socket = serverSocket.accept(); + Coordinator coordinator = new Coordinator(socket); + coordinator.start(); + coordinators.add(coordinator); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void shutdown() { + running = false; + for (Coordinator coordinator : coordinators) { + coordinator.shutdown(); + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/AbstractMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/AbstractMapper.java new file mode 100644 index 00000000..4f95dfb2 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/AbstractMapper.java @@ -0,0 +1,21 @@ +package adf_core_python.core.gateway.mapper; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import rescuecore2.config.Config; + +public abstract class AbstractMapper { + protected Class targetClass; + protected Logger logger; + + public AbstractMapper() { + this.targetClass = Object.class; + logger = LogManager.getLogger(this.getClass()); + } + + public Class getTargetClass() { + return targetClass; + } + + public abstract Config execMethod(String methodName, Config arguments); +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/MapperDict.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/MapperDict.java new file mode 100644 index 00000000..5cb4e168 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/MapperDict.java @@ -0,0 +1,51 @@ +package adf_core_python.core.gateway.mapper; + +import adf_core_python.core.component.module.AbstractModule; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.algorithm.DynamicClustering; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import adf_core_python.core.component.module.algorithm.StaticClustering; +import adf_core_python.core.component.module.complex.*; +import adf_core_python.core.gateway.mapper.module.AbstractModuleMapper; +import adf_core_python.core.gateway.mapper.module.algorithm.ClusteringMapper; +import adf_core_python.core.gateway.mapper.module.algorithm.DynamicClusteringMapper; +import adf_core_python.core.gateway.mapper.module.algorithm.PathPlanningMapper; +import adf_core_python.core.gateway.mapper.module.algorithm.StaticClusteringMapper; +import adf_core_python.core.gateway.mapper.module.complex.*; + +import java.util.HashMap; + +public class MapperDict { + private final HashMap, Class> mapperDict; + + public MapperDict() { + mapperDict = new HashMap<>(); + registerMapper(Clustering.class, ClusteringMapper.class); + registerMapper(DynamicClustering.class, DynamicClusteringMapper.class); + registerMapper(StaticClustering.class, StaticClusteringMapper.class); + + registerMapper(PathPlanning.class, PathPlanningMapper.class); + + registerMapper(TargetDetector.class, TargetDetectorMapper.class); + registerMapper(HumanDetector.class, HumanDetectorMapper.class); + registerMapper(RoadDetector.class, RoadDetectorMapper.class); + registerMapper(BuildingDetector.class, BuildingDetectorMapper.class); + + registerMapper(Search.class, SearchMapper.class); + + registerMapper(TargetAllocator.class, TargetAllocatorMapper.class); + registerMapper(AmbulanceTargetAllocator.class, AmbulanceTargetAllocatorMapper.class); + registerMapper(FireTargetAllocator.class, FireTargetAllocatorMapper.class); + registerMapper(PoliceTargetAllocator.class, PoliceTargetAllocatorMapper.class); + + registerMapper(AbstractModule.class, AbstractModuleMapper.class); + } + + public void registerMapper(Class component, Class mapper) { + mapperDict.put(component, mapper); + } + + public Class getMapper(Class component) { + return mapperDict.get(component); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/AbstractModuleMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/AbstractModuleMapper.java new file mode 100644 index 00000000..0c66bad4 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/AbstractModuleMapper.java @@ -0,0 +1,64 @@ +package adf_core_python.core.gateway.mapper.module; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.AbstractModule; +import adf_core_python.core.gateway.mapper.AbstractMapper; +import rescuecore2.config.Config; + +public class AbstractModuleMapper extends AbstractMapper { + protected final AbstractModule abstractModule; + private final PrecomputeData precomputeData; + private final MessageManager messageManager; + + public AbstractModuleMapper(AbstractModule abstractModule, PrecomputeData precomputeData, MessageManager messageManager) { + super(); + this.targetClass = AbstractModule.class; + this.abstractModule = abstractModule; + this.precomputeData = precomputeData; + this.messageManager = messageManager; + } + + @Override + public Config execMethod(String methodName, Config arguments) { + switch (methodName) { + case "precompute": + execPrecompute(); + break; + case "resume": + execResume(); + break; + case "preparate": + execPreparate(); + break; + case "updateInfo": + execUpdateInfo(); + break; + case "calc": + execCalc(); + break; + } + + return new Config(); + } + + public void execPrecompute() { + abstractModule.precompute(precomputeData); + } + + public void execResume() { + abstractModule.resume(precomputeData); + } + + public void execPreparate() { + abstractModule.preparate(); + } + + public void execUpdateInfo() { + abstractModule.updateInfo(messageManager); + } + + public void execCalc() { + abstractModule.calc(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/ClusteringMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/ClusteringMapper.java new file mode 100644 index 00000000..0e573115 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/ClusteringMapper.java @@ -0,0 +1,133 @@ +package adf_core_python.core.gateway.mapper.module.algorithm; + +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.gateway.mapper.module.AbstractModuleMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import rescuecore2.config.Config; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.worldmodel.EntityID; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class ClusteringMapper extends AbstractModuleMapper { + private final WorldInfo worldInfo; + + public ClusteringMapper(Clustering clustering, PrecomputeData precomputeData, MessageManager messageManager, WorldInfo worldInfo) { + super(clustering, precomputeData, messageManager); + this.targetClass = Clustering.class; + this.worldInfo = worldInfo; + } + + @Override + public Config execMethod(String methodName, Config arguments) { + Config result = super.execMethod(methodName, arguments); + if (methodName.equals("getClusterNumber")) { + result = execGetClusterNumber(); + } + if (methodName.equals("getClusterIndex(StandardEntity)")) { + result = execGetClusterIndex(worldInfo.getEntity(new EntityID(arguments.getIntValue("EntityID")))); + } + if (methodName.equals("getClusterIndex(EntityID)")) { + result = execGetClusterIndex(new EntityID(arguments.getIntValue("EntityID"))); + } + if (methodName.equals("getClusterEntities(int)")) { + result = execGetClusterEntities(arguments.getIntValue("Index")); + } + if (methodName.equals("getClusterEntityIDs(int)")) { + result = execGetClusterEntityIDs(arguments.getIntValue("Index")); + } + if (methodName.equals("getAllClusterEntities")) { + result = execGetAllClusterEntities(); + } + if (methodName.equals("getAllClusterEntityIDs")) { + result = execGetAllClusterEntityIDs(); + } + return result; + } + + private Config execGetClusterNumber() { + Clustering clustering = (Clustering) abstractModule; + int clusterNumber = clustering.getClusterNumber(); + Config result = new Config(); + result.setIntValue("ClusterNumber", clusterNumber); + return result; + } + + private Config execGetClusterIndex(StandardEntity standardEntity) { + Clustering clustering = (Clustering) abstractModule; + int clusterIndex = clustering.getClusterIndex(standardEntity); + Config result = new Config(); + result.setIntValue("ClusterIndex", clusterIndex); + return result; + } + + private Config execGetClusterIndex(EntityID entityID) { + Clustering clustering = (Clustering) abstractModule; + int clusterIndex = clustering.getClusterIndex(entityID); + Config result = new Config(); + result.setIntValue("ClusterIndex", clusterIndex); + return result; + } + + private Config execGetClusterEntities(int index) { + Clustering clustering = (Clustering) abstractModule; + Collection entities = clustering.getClusterEntities(index); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr = ""; + try { + jsonStr = objectMapper.writeValueAsString(entities.stream().map(e -> e.getID().getValue()).toArray()); + } catch (JsonProcessingException ignored) { + } + Config result = new Config(); + result.setValue("EntityIDs", jsonStr); + return result; + } + + private Config execGetClusterEntityIDs(int index) { + Clustering clustering = (Clustering) abstractModule; + Collection entities = clustering.getClusterEntityIDs(index); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr = ""; + try { + jsonStr = objectMapper.writeValueAsString(entities.stream().map(EntityID::getValue).toArray()); + } catch (JsonProcessingException ignored) { + } + Config result = new Config(); + result.setValue("EntityIDs", jsonStr); + return result; + } + + private Config execGetAllClusterEntities() { + Clustering clustering = (Clustering) abstractModule; + List> allClusterEntities = clustering.getAllClusterEntities(); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr = ""; + try { + jsonStr = objectMapper.writeValueAsString(allClusterEntities.stream().map(e -> e.stream().map(f -> f.getID().getValue()).collect(Collectors.toList())).toArray()); + } catch (JsonProcessingException ignored) { + } + Config result = new Config(); + result.setValue("EntityIDs", jsonStr); + return result; + } + + private Config execGetAllClusterEntityIDs() { + Clustering clustering = (Clustering) abstractModule; + List> allClusterEntityIDs = clustering.getAllClusterEntityIDs(); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr = ""; + try { + jsonStr = objectMapper.writeValueAsString(allClusterEntityIDs.stream().map(e -> e.stream().map(EntityID::getValue).collect(Collectors.toList())).toArray()); + } catch (JsonProcessingException ignored) { + } + Config result = new Config(); + result.setValue("EntityIDs", jsonStr); + return result; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/DynamicClusteringMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/DynamicClusteringMapper.java new file mode 100644 index 00000000..bc60931b --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/DynamicClusteringMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.algorithm; + +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.DynamicClustering; + +public class DynamicClusteringMapper extends ClusteringMapper { + public DynamicClusteringMapper(DynamicClustering dynamicClustering, PrecomputeData precomputeData, MessageManager messageManager, WorldInfo worldInfo) { + super(dynamicClustering, precomputeData, messageManager, worldInfo); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/PathPlanningMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/PathPlanningMapper.java new file mode 100644 index 00000000..00d6bfe4 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/PathPlanningMapper.java @@ -0,0 +1,140 @@ +package adf_core_python.core.gateway.mapper.module.algorithm; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import adf_core_python.core.gateway.mapper.module.AbstractModuleMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.EntityID; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +public class PathPlanningMapper extends AbstractModuleMapper { + public PathPlanningMapper(PathPlanning pathPlanning, PrecomputeData precomputeData, MessageManager messageManager) { + super(pathPlanning, precomputeData, messageManager); + } + + @Override + public Config execMethod(String methodName, Config arguments) { + Config result = super.execMethod(methodName, arguments); + if (methodName.equals("getResult")) { + result = execGetResult(); + } + if (methodName.equals("setFrom(EntityID)")) { + execSetFrom(new EntityID(arguments.getIntValue("EntityID"))); + } + if (methodName.equals("setDestination(Collection)")) { + ObjectMapper objectMapper = new ObjectMapper(); + Collection targets; + try { + targets = objectMapper.readValue(arguments.getValue("Targets"), new TypeReference>() { + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + execSetDestination(targets); + } + if (methodName.equals("getDistance")) { + result = execGetDistance(); + } + if (methodName.equals("getDistance(EntityID, EntityID)")) { + result = execGetDistance(new EntityID(arguments.getIntValue("From")), + new EntityID(arguments.getIntValue("Dest"))); + } + if (methodName.equals("getResult(EntityID, EntityID)")) { + result = execGetResult(new EntityID(arguments.getIntValue("From")), + new EntityID(arguments.getIntValue("Dest"))); + } + if (methodName.equals("getResult(EntityID, List[EntityID])")) { + ObjectMapper objectMapper = new ObjectMapper(); + Collection destinations; + try { + destinations = objectMapper.readValue(arguments.getValue("Destinations"), + new TypeReference>() { + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + result = execGetResult(new EntityID(arguments.getIntValue("From")), destinations); + } + return result; + } + + private Config execGetResult() { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + List entityIDs = pathPlanning.getResult(); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr; + try { + jsonStr = objectMapper.writeValueAsString(entityIDs); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + Config result = new Config(); + result.setValue("EntityIDs", jsonStr); + return result; + } + + private void execSetFrom(EntityID entityID) { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + pathPlanning.setFrom(entityID); + } + + private void execSetDestination(Collection targets) { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + pathPlanning.setDestination(targets); + } + + private Config execGetDistance() { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + Double distance = pathPlanning.getDistance(); + Config result = new Config(); + result.setValue("Distance", String.valueOf(distance)); + return result; + } + + private Config execGetDistance(EntityID from, EntityID dest) { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + Double distance = pathPlanning.getDistance(from, dest); + Config result = new Config(); + result.setValue("Distance", String.valueOf(distance)); + return result; + } + + private Config execGetResult(EntityID from, EntityID dest) { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + List entityIDs = pathPlanning.getResult(from, dest); + Config result = new Config(); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr; + try { + jsonStr = objectMapper.writeValueAsString(entityIDs.stream().map(EntityID::getValue).toArray()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + result.setValue("Result", String.valueOf(jsonStr)); + return result; + } + + private Config execGetResult(EntityID from, Collection destinations) { + PathPlanning pathPlanning = (PathPlanning) abstractModule; + pathPlanning.setFrom(from); + pathPlanning.setDestination(destinations); + List entityIDs = pathPlanning.getResult(); + Config result = new Config(); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonStr; + try { + jsonStr = objectMapper.writeValueAsString(entityIDs.stream().map(EntityID::getValue).toArray()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + result.setValue("Result", String.valueOf(jsonStr)); + return result; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/StaticClusteringMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/StaticClusteringMapper.java new file mode 100644 index 00000000..16ec9238 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/algorithm/StaticClusteringMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.algorithm; + +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.StaticClustering; + +public class StaticClusteringMapper extends ClusteringMapper { + public StaticClusteringMapper(StaticClustering staticClustering, PrecomputeData precomputeData, MessageManager messageManager, WorldInfo worldInfo) { + super(staticClustering, precomputeData, messageManager, worldInfo); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/AmbulanceTargetAllocatorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/AmbulanceTargetAllocatorMapper.java new file mode 100644 index 00000000..4e5df45a --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/AmbulanceTargetAllocatorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.AmbulanceTargetAllocator; + +public class AmbulanceTargetAllocatorMapper extends TargetAllocatorMapper { + public AmbulanceTargetAllocatorMapper(AmbulanceTargetAllocator ambulanceTargetAllocator, PrecomputeData precomputeData, MessageManager messageManager) { + super(ambulanceTargetAllocator, precomputeData, messageManager); + this.targetClass = AmbulanceTargetAllocator.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/BuildingDetectorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/BuildingDetectorMapper.java new file mode 100644 index 00000000..3a9bddde --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/BuildingDetectorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.BuildingDetector; + +public class BuildingDetectorMapper extends TargetDetectorMapper { + public BuildingDetectorMapper(BuildingDetector buildingDetector, PrecomputeData precomputeData, MessageManager messageManager) { + super(buildingDetector, precomputeData, messageManager); + this.targetClass = BuildingDetector.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/FireTargetAllocatorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/FireTargetAllocatorMapper.java new file mode 100644 index 00000000..62344717 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/FireTargetAllocatorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.FireTargetAllocator; + +public class FireTargetAllocatorMapper extends TargetAllocatorMapper { + public FireTargetAllocatorMapper(FireTargetAllocator fireTargetAllocator, PrecomputeData precomputeData, MessageManager messageManager) { + super(fireTargetAllocator, precomputeData, messageManager); + this.targetClass = FireTargetAllocator.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/HumanDetectorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/HumanDetectorMapper.java new file mode 100644 index 00000000..078fe162 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/HumanDetectorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.HumanDetector; + +public class HumanDetectorMapper extends TargetDetectorMapper { + public HumanDetectorMapper(HumanDetector humanDetector, PrecomputeData precomputeData, MessageManager messageManager) { + super(humanDetector, precomputeData, messageManager); + this.targetClass = HumanDetector.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/PoliceTargetAllocatorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/PoliceTargetAllocatorMapper.java new file mode 100644 index 00000000..1d6dd418 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/PoliceTargetAllocatorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.PoliceTargetAllocator; + +public class PoliceTargetAllocatorMapper extends TargetAllocatorMapper { + public PoliceTargetAllocatorMapper(PoliceTargetAllocator policeTargetAllocator, PrecomputeData precomputeData, MessageManager messageManager) { + super(policeTargetAllocator, precomputeData, messageManager); + this.targetClass = PoliceTargetAllocator.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/RoadDetectorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/RoadDetectorMapper.java new file mode 100644 index 00000000..412634f2 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/RoadDetectorMapper.java @@ -0,0 +1,12 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.RoadDetector; + +public class RoadDetectorMapper extends TargetDetectorMapper { + public RoadDetectorMapper(RoadDetector roadDetector, PrecomputeData precomputeData, MessageManager messageManager) { + super(roadDetector, precomputeData, messageManager); + this.targetClass = RoadDetector.class; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/SearchMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/SearchMapper.java new file mode 100644 index 00000000..7cf6d41e --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/SearchMapper.java @@ -0,0 +1,11 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.Search; + +public class SearchMapper extends TargetDetectorMapper { + public SearchMapper(Search search, PrecomputeData precomputeData, MessageManager messageManager) { + super(search, precomputeData, messageManager); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetAllocatorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetAllocatorMapper.java new file mode 100644 index 00000000..d211833a --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetAllocatorMapper.java @@ -0,0 +1,34 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.TargetAllocator; +import adf_core_python.core.gateway.mapper.module.AbstractModuleMapper; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.EntityID; + +import java.util.Map; + +public class TargetAllocatorMapper extends AbstractModuleMapper { + public TargetAllocatorMapper(TargetAllocator targetAllocator, PrecomputeData precomputeData, MessageManager messageManager) { + super(targetAllocator, precomputeData, messageManager); + this.targetClass = TargetAllocator.class; + } + + @Override + public Config execMethod(String methodName, Config arguments) { + Config result = super.execMethod(methodName, arguments); + if (methodName.equals("getResult")) { + result = execGetResult(); + } + return result; + } + + public Config execGetResult() { + TargetAllocator targetAllocator = (TargetAllocator) abstractModule; + Map result = targetAllocator.getResult(); + Config response = new Config(); + result.forEach((k, v) -> response.setValue(String.valueOf(k.getValue()), String.valueOf(v.getValue()))); + return response; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetDetectorMapper.java b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetDetectorMapper.java new file mode 100644 index 00000000..caaba9b1 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/mapper/module/complex/TargetDetectorMapper.java @@ -0,0 +1,35 @@ +package adf_core_python.core.gateway.mapper.module.complex; + +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.TargetDetector; +import adf_core_python.core.gateway.mapper.module.AbstractModuleMapper; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.EntityID; + +public class TargetDetectorMapper extends AbstractModuleMapper { + public TargetDetectorMapper(TargetDetector targetDetector, PrecomputeData precomputeData, MessageManager messageManager) { + super(targetDetector, precomputeData, messageManager); + this.targetClass = TargetDetector.class; + } + + @Override + public Config execMethod(String methodName, Config arguments) { + Config result = super.execMethod(methodName, arguments); + if (methodName.equals("getTarget")) { + result = execGetTarget(); + } + return result; + } + + public Config execGetTarget() { + TargetDetector targetDetector = (TargetDetector) abstractModule; + EntityID entityID = targetDetector.getTarget(); + Config result = new Config(); + result.setIntValue("EntityID", -1); + if (entityID != null) { + result.setIntValue("EntityID", entityID.getValue()); + } + return result; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/AMAgent.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMAgent.java new file mode 100644 index 00000000..17775b8b --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMAgent.java @@ -0,0 +1,71 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.EntityListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +public class AMAgent extends AbstractMessage { + private final EntityIDComponent agentID; + private final EntityListComponent entities; + private final ConfigComponent config; + private final IntComponent mode; + + public AMAgent(EntityID agentID, Collection entities, Config config, int mode) { + this(); + this.agentID.setValue(agentID); + this.entities.setEntities(entities); + this.config.setConfig(config); + this.mode.setValue(mode); + } + + public AMAgent(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public AMAgent(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private AMAgent() { + super(ModuleMessageURN.AM_AGENT); + this.agentID = new EntityIDComponent(ModuleMessageComponentURN.AgentID); + this.entities = new EntityListComponent(ModuleMessageComponentURN.Entities); + this.config = new ConfigComponent(ModuleMessageComponentURN.Config); + this.mode = new IntComponent(ModuleMessageComponentURN.Mode); + addMessageComponent(agentID); + addMessageComponent(entities); + addMessageComponent(config); + addMessageComponent(mode); + } + + public EntityID getAgentID() { + return this.agentID.getValue(); + } + + public List getEntities() { + return this.entities.getEntities(); + } + + public Config getConfig() { + return this.config.getConfig(); + } + + public int getMode() { + return this.mode.getValue(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/AMExec.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMExec.java new file mode 100644 index 00000000..5f6da2df --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMExec.java @@ -0,0 +1,57 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto; + +import java.io.IOException; +import java.io.InputStream; + +public class AMExec extends AbstractMessage { + private final StringComponent moduleID; + private final StringComponent methodName; + private final ConfigComponent arguments; + + public AMExec(String moduleID, String methodName, Config arguments) { + this(); + this.moduleID.setValue(moduleID); + this.methodName.setValue(methodName); + this.arguments.setConfig(arguments); + } + + public AMExec(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public AMExec(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private AMExec() { + super(ModuleMessageURN.AM_MODULE); + this.moduleID = new StringComponent(ModuleMessageComponentURN.ModuleID); + this.methodName = new StringComponent(ModuleMessageComponentURN.MethodName); + this.arguments = new ConfigComponent(ModuleMessageComponentURN.Arguments); + addMessageComponent(moduleID); + addMessageComponent(methodName); + addMessageComponent(arguments); + } + + public String getModuleID() { + return this.moduleID.getValue(); + } + + public String getMethodName() { + return this.methodName.getValue(); + } + + public Config getArguments() { + return this.arguments.getConfig(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/AMModule.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMModule.java new file mode 100644 index 00000000..10217ed0 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMModule.java @@ -0,0 +1,55 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto; + +import java.io.IOException; +import java.io.InputStream; + +public class AMModule extends AbstractMessage { + private final StringComponent moduleID; + private final StringComponent moduleName; + private final StringComponent defaultClassName; + + public AMModule(String moduleID, String moduleName, String defaultClassName) { + this(); + this.moduleID.setValue(moduleID); + this.moduleName.setValue(moduleName); + this.defaultClassName.setValue(defaultClassName); + } + + public AMModule(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public AMModule(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private AMModule() { + super(ModuleMessageURN.AM_MODULE); + this.moduleID = new StringComponent(ModuleMessageComponentURN.ModuleID); + this.moduleName = new StringComponent(ModuleMessageComponentURN.ModuleName); + this.defaultClassName = new StringComponent(ModuleMessageComponentURN.DefaultClassName); + addMessageComponent(moduleID); + addMessageComponent(moduleName); + addMessageComponent(defaultClassName); + } + + public String getModuleID() { + return this.moduleID.getValue(); + } + + public String getModuleName() { + return this.moduleName.getValue(); + } + + public String getDefaultClassName() { + return this.defaultClassName.getValue(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/AMUpdate.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMUpdate.java new file mode 100644 index 00000000..c3c21b5a --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/AMUpdate.java @@ -0,0 +1,61 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.Command; +import rescuecore2.messages.components.ChangeSetComponent; +import rescuecore2.messages.components.CommandListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto; +import rescuecore2.worldmodel.ChangeSet; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +public class AMUpdate extends AbstractMessage { + private final IntComponent time; + private final ChangeSetComponent changed; + private final CommandListComponent heard; + + public AMUpdate(int time, ChangeSet changed, Collection heard) { + this(); + this.time.setValue(time); + this.changed.setChangeSet(changed); + this.heard.setCommands(heard); + } + + public AMUpdate(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public AMUpdate(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private AMUpdate() { + super(ModuleMessageURN.AM_UPDATE); + this.time = new IntComponent(ModuleMessageComponentURN.Time); + this.changed = new ChangeSetComponent(ModuleMessageComponentURN.Changed); + this.heard = new CommandListComponent(ModuleMessageComponentURN.Heard); + addMessageComponent(this.time); + addMessageComponent(this.changed); + addMessageComponent(this.heard); + } + + public int getTime() { + return this.time.getValue(); + } + + public ChangeSet getChanged() { + return this.changed.getChangeSet(); + } + + public List getHeard() { + return this.heard.getCommands(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/MAExecResponse.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/MAExecResponse.java new file mode 100644 index 00000000..ecd26df9 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/MAExecResponse.java @@ -0,0 +1,49 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto; + +import java.io.IOException; +import java.io.InputStream; + +public class MAExecResponse extends AbstractMessage { + private final StringComponent moduleID; + private final ConfigComponent result; + + public MAExecResponse(String moduleID, Config result) { + this(); + this.moduleID.setValue(moduleID); + this.result.setConfig(result); + } + + public MAExecResponse(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public MAExecResponse(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private MAExecResponse() { + super(ModuleMessageURN.MA_EXEC_RESPONSE); + this.moduleID = new StringComponent(ModuleMessageComponentURN.ModuleID); + this.result = new ConfigComponent(ModuleMessageComponentURN.Result); + addMessageComponent(moduleID); + addMessageComponent(result); + } + + public String getModuleID() { + return this.moduleID.getValue(); + } + + public Config getResult() { + return this.result.getConfig(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/MAModuleResponse.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/MAModuleResponse.java new file mode 100644 index 00000000..bbd50d96 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/MAModuleResponse.java @@ -0,0 +1,47 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageComponentURN; +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto; + +import java.io.IOException; +import java.io.InputStream; + +public class MAModuleResponse extends AbstractMessage { + private final StringComponent moduleID; + private final StringComponent className; + + public MAModuleResponse(String moduleID, String className) { + this(); + this.moduleID.setValue(moduleID); + this.className.setValue(className); + } + + public MAModuleResponse(InputStream inputStream) throws IOException { + this(); + read(inputStream); + } + + public MAModuleResponse(RCRSProto.MessageProto messageProto) { + this(); + fromMessageProto(messageProto); + } + + private MAModuleResponse() { + super(ModuleMessageURN.MA_MODULE_RESPONSE); + this.moduleID = new StringComponent(ModuleMessageComponentURN.ModuleID); + this.className = new StringComponent(ModuleMessageComponentURN.ClassName); + addMessageComponent(moduleID); + addMessageComponent(className); + } + + public String getModuleID() { + return this.moduleID.getValue(); + } + + public String getClassName() { + return this.className.getValue(); + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/ModuleControlMessageFactory.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/ModuleControlMessageFactory.java new file mode 100644 index 00000000..c6b4ac75 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/ModuleControlMessageFactory.java @@ -0,0 +1,42 @@ +package adf_core_python.core.gateway.message; + +import adf_core_python.core.gateway.message.urn.ModuleMessageURN; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +import java.io.IOException; +import java.io.InputStream; + +public class ModuleControlMessageFactory { + public static Message makeMessage(int urn, InputStream inputStream) throws IOException { + return makeMessage(ModuleMessageURN.fromInt(urn), inputStream); + } + + public static Message makeMessage(ModuleMessageURN urn, InputStream inputStream) throws IOException { + switch (urn) { + case AM_AGENT -> new AMAgent(inputStream); + case AM_MODULE -> new AMModule(inputStream); + case MA_MODULE_RESPONSE -> new MAModuleResponse(inputStream); + case AM_UPDATE -> new AMUpdate(inputStream); + case AM_EXEC -> new AMExec(inputStream); + } + return null; + } + + public static Message makeMessage(int urn, MessageProto messageProto) throws IOException { + return makeMessage(ModuleMessageURN.fromInt(urn), messageProto); + } + + public static Message makeMessage(ModuleMessageURN urn, MessageProto messageProto) { + if (urn.equals(ModuleMessageURN.AM_AGENT)) { + return new AMAgent(messageProto); + } else if (urn.equals(ModuleMessageURN.AM_MODULE)) { + return new AMModule(messageProto); + } else if (urn.equals(ModuleMessageURN.AM_UPDATE)) { + return new AMUpdate(messageProto); + } else if (urn.equals(ModuleMessageURN.AM_EXEC)) { + return new AMExec(messageProto); + } + return null; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageComponentURN.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageComponentURN.java new file mode 100644 index 00000000..c89232db --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageComponentURN.java @@ -0,0 +1,52 @@ +package adf_core_python.core.gateway.message.urn; + +import rescuecore2.URN; + +import java.util.Map; + +public enum ModuleMessageComponentURN implements URN { + AgentID(0x0400 | 1, "Agent ID"), + Entities(0x0400 | 2, "Entities"), + Config(0x0400 | 3, "Entities"), + Mode(0x0400 | 4, "Entities"), + ModuleID(0x0400 | 5, "Module ID"), + ModuleName(0x0400 | 6, "Module Name"), + DefaultClassName(0x0400 | 7, "Default Class Name"), + ClassName(0x0400 | 8, "Class Name"), + Time(0x0400 | 9, "Time"), + Changed(0x0400 | 10, "Changed"), + Heard(0x0400 | 11, "Heard"), + MethodName(0x0400 | 12, "Method Name"), + Arguments(0x0400 | 13, "Arguments"), + Result(0x0400 | 14, "Result"); + + + public static final Map MAP = URN.generateMap(ModuleMessageComponentURN.class); + public static final Map MAP_STR = URN + .generateMapStr(ModuleMessageComponentURN.class); + private final int urnId; + private final String urnStr; + + ModuleMessageComponentURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + public static ModuleMessageComponentURN fromInt(int urnId) { + return MAP.get(urnId); + } + + public static ModuleMessageComponentURN fromString(String urnStr) { + return MAP_STR.get(urnStr); + } + + @Override + public int getURNId() { + return urnId; + } + + @Override + public String getURNStr() { + return urnStr; + } +} diff --git a/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageURN.java b/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageURN.java new file mode 100644 index 00000000..83358b5d --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/core/gateway/message/urn/ModuleMessageURN.java @@ -0,0 +1,42 @@ +package adf_core_python.core.gateway.message.urn; + +import rescuecore2.URN; + +import java.util.Map; + +public enum ModuleMessageURN implements URN { + AM_AGENT(0x0300 | 1, "urn:adf_core_python.gateway.messages:am_agent"), + AM_MODULE(0x0300 | 2, "urn:adf_core_python.gateway.messages:am_module"), + MA_MODULE_RESPONSE(0x0300 | 3, "urn:adf_core_python.gateway.messages:am_module_response"), + AM_UPDATE(0x0300 | 4, "urn:adf_core_python.gateway.messages:am_update"), + AM_EXEC(0x0300 | 5, "urn:adf_core_python.gateway.messages:am_exec"), + MA_EXEC_RESPONSE(0x0300 | 6, "urn:adf_core_python.gateway.messages:am_exec_response"); + + public static final Map MAP = URN.generateMap(ModuleMessageURN.class); + public static final Map MAP_STR = URN.generateMapStr(ModuleMessageURN.class); + private final int urnId; + private final String urnStr; + + private ModuleMessageURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + public static ModuleMessageURN fromInt(int s) { + return MAP.get(s); + } + + public static ModuleMessageURN fromString(String urn) { + return MAP_STR.get(urn); + } + + @Override + public int getURNId() { + return urnId; + } + + @Override + public String getURNStr() { + return urnStr; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionClear.java b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionClear.java new file mode 100644 index 00000000..bb415cb3 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionClear.java @@ -0,0 +1,803 @@ +package adf_core_python.impl.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.action.common.ActionMove; +import adf.core.agent.action.common.ActionRest; +import adf.core.agent.action.police.ActionClear; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import com.google.common.collect.Lists; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; +import java.util.stream.Collectors; + +public class DefaultExtActionClear extends ExtAction { + + private PathPlanning pathPlanning; + + private int clearDistance; + private int forcedMove; + private int thresholdRest; + private int kernelTime; + + private EntityID target; + private Map> movePointCache; + private int oldClearX; + private int oldClearY; + private int count; + + public DefaultExtActionClear(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.clearDistance = si.getClearRepairDistance(); + this.forcedMove = developData + .getInteger("adf.impl.extaction.DefaultExtActionClear.forcedMove", 3); + this.thresholdRest = developData + .getInteger("adf.impl.extaction.DefaultExtActionClear.rest", 100); + + this.target = null; + this.movePointCache = new HashMap<>(); + this.oldClearX = 0; + this.oldClearY = 0; + this.count = 0; + + switch (si.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultExtActionClear.PathPlanning", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + } + + + @Override + public ExtAction precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.pathPlanning.precompute(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.pathPlanning.resume(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.pathPlanning.preparate(); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.pathPlanning.updateInfo(messageManager); + return this; + } + + + @Override + public ExtAction setTarget(EntityID target) { + this.target = null; + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity != null) { + if (entity instanceof Road) { + this.target = target; + } else if (entity.getStandardURN().equals(StandardEntityURN.BLOCKADE)) { + this.target = ((Blockade) entity).getPosition(); + } else if (entity instanceof Building) { + this.target = target; + } + } + return this; + } + + + @Override + public ExtAction calc() { + this.result = null; + PoliceForce policeForce = (PoliceForce) this.agentInfo.me(); + + if (this.needRest(policeForce)) { + List list = new ArrayList<>(); + if (this.target != null) { + list.add(this.target); + } + this.result = this.calcRest(policeForce, this.pathPlanning, list); + if (this.result != null) { + return this; + } + } + + if (this.target == null) { + return this; + } + EntityID agentPosition = policeForce.getPosition(); + StandardEntity targetEntity = this.worldInfo.getEntity(this.target); + StandardEntity positionEntity = Objects + .requireNonNull(this.worldInfo.getEntity(agentPosition)); + if (targetEntity == null || !(targetEntity instanceof Area)) { + return this; + } + if (positionEntity instanceof Road) { + this.result = this.getRescueAction(policeForce, (Road) positionEntity); + if (this.result != null) { + return this; + } + } + if (agentPosition.equals(this.target)) { + this.result = this.getAreaClearAction(policeForce, targetEntity); + } else if (((Area) targetEntity).getEdgeTo(agentPosition) != null) { + this.result = this.getNeighbourPositionAction(policeForce, + (Area) targetEntity); + } else { + List path = this.pathPlanning.getResult(agentPosition, + this.target); + if (path != null && path.size() > 0) { + int index = path.indexOf(agentPosition); + if (index == -1) { + Area area = (Area) positionEntity; + for (int i = 0; i < path.size(); i++) { + if (area.getEdgeTo(path.get(i)) != null) { + index = i; + break; + } + } + } else if (index >= 0) { + index++; + } + if (index >= 0 && index < (path.size())) { + StandardEntity entity = this.worldInfo.getEntity(path.get(index)); + this.result = this.getNeighbourPositionAction(policeForce, + (Area) entity); + if (this.result != null + && this.result.getClass() == ActionMove.class) { + if (!((ActionMove) this.result).getUsePosition()) { + this.result = null; + } + } + } + if (this.result == null) { + this.result = new ActionMove(path); + } + } + } + return this; + } + + + private Action getRescueAction(PoliceForce police, Road road) { + if (!road.isBlockadesDefined()) { + return null; + } + Collection blockades = this.worldInfo.getBlockades(road).stream() + .filter(Blockade::isApexesDefined).collect(Collectors.toSet()); + Collection agents = this.worldInfo.getEntitiesOfType( + StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.FIRE_BRIGADE); + + double policeX = police.getX(); + double policeY = police.getY(); + double minDistance = Double.MAX_VALUE; + Action moveAction = null; + for (StandardEntity entity : agents) { + Human human = (Human) entity; + if (!human.isPositionDefined() + || human.getPosition().getValue() != road.getID().getValue()) { + continue; + } + double humanX = human.getX(); + double humanY = human.getY(); + ActionClear actionClear = null; + for (Blockade blockade : blockades) { + if (!this.isInside(humanX, humanY, blockade.getApexes())) { + continue; + } + double distance = this.getDistance(policeX, policeY, humanX, humanY); + if (this.intersect(policeX, policeY, humanX, humanY, road)) { + Action action = this.getIntersectEdgeAction(policeX, policeY, humanX, + humanY, road); + if (action == null) { + continue; + } + if (action.getClass() == ActionClear.class) { + if (actionClear == null) { + actionClear = (ActionClear) action; + continue; + } + if (actionClear.getTarget() != null) { + Blockade another = (Blockade) this.worldInfo + .getEntity(actionClear.getTarget()); + if (another != null && this.intersect(blockade, another)) { + return new ActionClear(another); + } + int anotherDistance = this.worldInfo.getDistance(police, another); + int blockadeDistance = this.worldInfo.getDistance(police, + blockade); + if (anotherDistance > blockadeDistance) { + return action; + } + } + return actionClear; + } else if (action.getClass() == ActionMove.class + && distance < minDistance) { + minDistance = distance; + moveAction = action; + } + } else if (this.intersect(policeX, policeY, humanX, humanY, blockade)) { + Vector2D vector = this + .scaleClear(this.getVector(policeX, policeY, humanX, humanY)); + int clearX = (int) (policeX + vector.getX()); + int clearY = (int) (policeY + vector.getY()); + vector = this.scaleBackClear(vector); + int startX = (int) (policeX + vector.getX()); + int startY = (int) (policeY + vector.getY()); + if (this.intersect(startX, startY, clearX, clearY, blockade)) { + if (actionClear == null) { + actionClear = new ActionClear(clearX, clearY, blockade); + } else { + if (actionClear.getTarget() != null) { + Blockade another = (Blockade) this.worldInfo + .getEntity(actionClear.getTarget()); + if (another != null && this.intersect(blockade, another)) { + return new ActionClear(another); + } + int distance1 = this.worldInfo.getDistance(police, another); + int distance2 = this.worldInfo.getDistance(police, blockade); + if (distance1 > distance2) { + return new ActionClear(clearX, clearY, blockade); + } + } + return actionClear; + } + } else if (distance < minDistance) { + minDistance = distance; + moveAction = new ActionMove(Lists.newArrayList(road.getID()), + (int) humanX, (int) humanY); + } + } + } + if (actionClear != null) { + return actionClear; + } + } + return moveAction; + } + + + private Action getAreaClearAction(PoliceForce police, + StandardEntity targetEntity) { + if (targetEntity instanceof Building) { + return null; + } + Road road = (Road) targetEntity; + if (!road.isBlockadesDefined() || road.getBlockades().isEmpty()) { + return null; + } + Collection blockades = this.worldInfo.getBlockades(road).stream() + .filter(Blockade::isApexesDefined).collect(Collectors.toSet()); + int minDistance = Integer.MAX_VALUE; + Blockade clearBlockade = null; + for (Blockade blockade : blockades) { + for (Blockade another : blockades) { + if (!blockade.getID().equals(another.getID()) + && this.intersect(blockade, another)) { + int distance1 = this.worldInfo.getDistance(police, blockade); + int distance2 = this.worldInfo.getDistance(police, another); + if (distance1 <= distance2 && distance1 < minDistance) { + minDistance = distance1; + clearBlockade = blockade; + } else if (distance2 < minDistance) { + minDistance = distance2; + clearBlockade = another; + } + } + } + } + if (clearBlockade != null) { + if (minDistance < this.clearDistance) { + return new ActionClear(clearBlockade); + } else { + return new ActionMove(Lists.newArrayList(police.getPosition()), + clearBlockade.getX(), clearBlockade.getY()); + } + } + double agentX = police.getX(); + double agentY = police.getY(); + clearBlockade = null; + Double minPointDistance = Double.MAX_VALUE; + int clearX = 0; + int clearY = 0; + for (Blockade blockade : blockades) { + int[] apexes = blockade.getApexes(); + for (int i = 0; i < (apexes.length - 2); i += 2) { + double distance = this.getDistance(agentX, agentY, apexes[i], + apexes[i + 1]); + if (distance < minPointDistance) { + clearBlockade = blockade; + minPointDistance = distance; + clearX = apexes[i]; + clearY = apexes[i + 1]; + } + } + } + if (clearBlockade != null) { + if (minPointDistance < this.clearDistance) { + Vector2D vector = this + .scaleClear(this.getVector(agentX, agentY, clearX, clearY)); + clearX = (int) (agentX + vector.getX()); + clearY = (int) (agentY + vector.getY()); + return new ActionClear(clearX, clearY, clearBlockade); + } + return new ActionMove(Lists.newArrayList(police.getPosition()), clearX, + clearY); + } + return null; + } + + + private Action getNeighbourPositionAction(PoliceForce police, Area target) { + double agentX = police.getX(); + double agentY = police.getY(); + StandardEntity position = Objects + .requireNonNull(this.worldInfo.getPosition(police)); + Edge edge = target.getEdgeTo(position.getID()); + if (edge == null) { + return null; + } + if (position instanceof Road) { + Road road = (Road) position; + if (road.isBlockadesDefined() && road.getBlockades().size() > 0) { + double midX = (edge.getStartX() + edge.getEndX()) / 2; + double midY = (edge.getStartY() + edge.getEndY()) / 2; + if (this.intersect(agentX, agentY, midX, midY, road)) { + return this.getIntersectEdgeAction(agentX, agentY, edge, road); + } + ActionClear actionClear = null; + ActionMove actionMove = null; + Vector2D vector = this + .scaleClear(this.getVector(agentX, agentY, midX, midY)); + int clearX = (int) (agentX + vector.getX()); + int clearY = (int) (agentY + vector.getY()); + vector = this.scaleBackClear(vector); + int startX = (int) (agentX + vector.getX()); + int startY = (int) (agentY + vector.getY()); + for (Blockade blockade : this.worldInfo.getBlockades(road)) { + if (blockade == null || !blockade.isApexesDefined()) { + continue; + } + if (this.intersect(startX, startY, midX, midY, blockade)) { + if (this.intersect(startX, startY, clearX, clearY, blockade)) { + if (actionClear == null) { + actionClear = new ActionClear(clearX, clearY, blockade); + if (this.equalsPoint(this.oldClearX, this.oldClearY, clearX, + clearY)) { + if (this.count >= this.forcedMove) { + this.count = 0; + return new ActionMove(Lists.newArrayList(road.getID()), + clearX, clearY); + } + this.count++; + } + this.oldClearX = clearX; + this.oldClearY = clearY; + } else { + if (actionClear.getTarget() != null) { + Blockade another = (Blockade) this.worldInfo + .getEntity(actionClear.getTarget()); + if (another != null && this.intersect(blockade, another)) { + return new ActionClear(another); + } + } + return actionClear; + } + } else if (actionMove == null) { + actionMove = new ActionMove(Lists.newArrayList(road.getID()), + (int) midX, (int) midY); + } + } + } + if (actionClear != null) { + return actionClear; + } else if (actionMove != null) { + return actionMove; + } + } + } + if (target instanceof Road) { + Road road = (Road) target; + if (!road.isBlockadesDefined() || road.getBlockades().isEmpty()) { + return new ActionMove( + Lists.newArrayList(position.getID(), target.getID())); + } + Blockade clearBlockade = null; + Double minPointDistance = Double.MAX_VALUE; + int clearX = 0; + int clearY = 0; + for (EntityID id : road.getBlockades()) { + Blockade blockade = (Blockade) this.worldInfo.getEntity(id); + if (blockade != null && blockade.isApexesDefined()) { + int[] apexes = blockade.getApexes(); + for (int i = 0; i < (apexes.length - 2); i += 2) { + double distance = this.getDistance(agentX, agentY, apexes[i], + apexes[i + 1]); + if (distance < minPointDistance) { + clearBlockade = blockade; + minPointDistance = distance; + clearX = apexes[i]; + clearY = apexes[i + 1]; + } + } + } + } + if (clearBlockade != null && minPointDistance < this.clearDistance) { + Vector2D vector = this + .scaleClear(this.getVector(agentX, agentY, clearX, clearY)); + clearX = (int) (agentX + vector.getX()); + clearY = (int) (agentY + vector.getY()); + if (this.equalsPoint(this.oldClearX, this.oldClearY, clearX, clearY)) { + if (this.count >= this.forcedMove) { + this.count = 0; + return new ActionMove(Lists.newArrayList(road.getID()), clearX, + clearY); + } + this.count++; + } + this.oldClearX = clearX; + this.oldClearY = clearY; + return new ActionClear(clearX, clearY, clearBlockade); + } + } + return new ActionMove(Lists.newArrayList(position.getID(), target.getID())); + } + + + private Action getIntersectEdgeAction(double agentX, double agentY, Edge edge, + Road road) { + double midX = (edge.getStartX() + edge.getEndX()) / 2; + double midY = (edge.getStartY() + edge.getEndY()) / 2; + return this.getIntersectEdgeAction(agentX, agentY, midX, midY, road); + } + + + private Action getIntersectEdgeAction(double agentX, double agentY, + double pointX, double pointY, Road road) { + Set movePoints = this.getMovePoints(road); + Point2D bestPoint = null; + double bastDistance = Double.MAX_VALUE; + for (Point2D p : movePoints) { + if (!this.intersect(agentX, agentY, p.getX(), p.getY(), road)) { + if (!this.intersect(pointX, pointY, p.getX(), p.getY(), road)) { + double distance = this.getDistance(pointX, pointY, p.getX(), + p.getY()); + if (distance < bastDistance) { + bestPoint = p; + bastDistance = distance; + } + } + } + } + if (bestPoint != null) { + double pX = bestPoint.getX(); + double pY = bestPoint.getY(); + if (!road.isBlockadesDefined()) { + return new ActionMove(Lists.newArrayList(road.getID()), (int) pX, + (int) pY); + } + ActionClear actionClear = null; + ActionMove actionMove = null; + Vector2D vector = this.scaleClear(this.getVector(agentX, agentY, pX, pY)); + int clearX = (int) (agentX + vector.getX()); + int clearY = (int) (agentY + vector.getY()); + vector = this.scaleBackClear(vector); + int startX = (int) (agentX + vector.getX()); + int startY = (int) (agentY + vector.getY()); + for (Blockade blockade : this.worldInfo.getBlockades(road)) { + if (this.intersect(startX, startY, pX, pY, blockade)) { + if (this.intersect(startX, startY, clearX, clearY, blockade)) { + if (actionClear == null) { + actionClear = new ActionClear(clearX, clearY, blockade); + } else { + if (actionClear.getTarget() != null) { + Blockade another = (Blockade) this.worldInfo + .getEntity(actionClear.getTarget()); + if (another != null && this.intersect(blockade, another)) { + return new ActionClear(another); + } + } + return actionClear; + } + } else if (actionMove == null) { + actionMove = new ActionMove(Lists.newArrayList(road.getID()), + (int) pX, (int) pY); + } + } + } + if (actionClear != null) { + return actionClear; + } else if (actionMove != null) { + return actionMove; + } + } + Action action = this.getAreaClearAction((PoliceForce) this.agentInfo.me(), + road); + if (action == null) { + action = new ActionMove(Lists.newArrayList(road.getID()), (int) pointX, + (int) pointY); + } + return action; + } + + + private boolean equalsPoint(double p1X, double p1Y, double p2X, double p2Y) { + return this.equalsPoint(p1X, p1Y, p2X, p2Y, 1000.0D); + } + + + private boolean equalsPoint(double p1X, double p1Y, double p2X, double p2Y, + double range) { + return (p2X - range < p1X && p1X < p2X + range) + && (p2Y - range < p1Y && p1Y < p2Y + range); + } + + + private boolean isInside(double pX, double pY, int[] apex) { + Point2D p = new Point2D(pX, pY); + Vector2D v1 = (new Point2D(apex[apex.length - 2], apex[apex.length - 1])) + .minus(p); + Vector2D v2 = (new Point2D(apex[0], apex[1])).minus(p); + double theta = this.getAngle(v1, v2); + + for (int i = 0; i < apex.length - 2; i += 2) { + v1 = (new Point2D(apex[i], apex[i + 1])).minus(p); + v2 = (new Point2D(apex[i + 2], apex[i + 3])).minus(p); + theta += this.getAngle(v1, v2); + } + return Math.round(Math.abs((theta / 2) / Math.PI)) >= 1; + } + + + private boolean intersect(double agentX, double agentY, double pointX, + double pointY, Area area) { + for (Edge edge : area.getEdges()) { + double startX = edge.getStartX(); + double startY = edge.getStartY(); + double endX = edge.getEndX(); + double endY = edge.getEndY(); + if (java.awt.geom.Line2D.linesIntersect(agentX, agentY, pointX, pointY, + startX, startY, endX, endY)) { + double midX = (edge.getStartX() + edge.getEndX()) / 2; + double midY = (edge.getStartY() + edge.getEndY()) / 2; + if (!equalsPoint(pointX, pointY, midX, midY) + && !equalsPoint(agentX, agentY, midX, midY)) { + return true; + } + } + } + return false; + } + + + private boolean intersect(Blockade blockade, Blockade another) { + if (blockade.isApexesDefined() && another.isApexesDefined()) { + int[] apexes0 = blockade.getApexes(); + int[] apexes1 = another.getApexes(); + for (int i = 0; i < (apexes0.length - 2); i += 2) { + for (int j = 0; j < (apexes1.length - 2); j += 2) { + if (java.awt.geom.Line2D.linesIntersect(apexes0[i], apexes0[i + 1], + apexes0[i + 2], apexes0[i + 3], apexes1[j], apexes1[j + 1], + apexes1[j + 2], apexes1[j + 3])) { + return true; + } + } + } + for (int i = 0; i < (apexes0.length - 2); i += 2) { + if (java.awt.geom.Line2D.linesIntersect(apexes0[i], apexes0[i + 1], + apexes0[i + 2], apexes0[i + 3], apexes1[apexes1.length - 2], + apexes1[apexes1.length - 1], apexes1[0], apexes1[1])) { + return true; + } + } + for (int j = 0; j < (apexes1.length - 2); j += 2) { + if (java.awt.geom.Line2D.linesIntersect(apexes0[apexes0.length - 2], + apexes0[apexes0.length - 1], apexes0[0], apexes0[1], apexes1[j], + apexes1[j + 1], apexes1[j + 2], apexes1[j + 3])) { + return true; + } + } + } + return false; + } + + + private boolean intersect(double agentX, double agentY, double pointX, + double pointY, Blockade blockade) { + List lines = GeometryTools2D.pointsToLines( + GeometryTools2D.vertexArrayToPoints(blockade.getApexes()), true); + for (Line2D line : lines) { + Point2D start = line.getOrigin(); + Point2D end = line.getEndPoint(); + double startX = start.getX(); + double startY = start.getY(); + double endX = end.getX(); + double endY = end.getY(); + if (java.awt.geom.Line2D.linesIntersect(agentX, agentY, pointX, pointY, + startX, startY, endX, endY)) { + return true; + } + } + return false; + } + + + private double getDistance(double fromX, double fromY, double toX, + double toY) { + double dx = toX - fromX; + double dy = toY - fromY; + return Math.hypot(dx, dy); + } + + + private double getAngle(Vector2D v1, Vector2D v2) { + double flag = (v1.getX() * v2.getY()) - (v1.getY() * v2.getX()); + double angle = Math.acos(((v1.getX() * v2.getX()) + (v1.getY() * v2.getY())) + / (v1.getLength() * v2.getLength())); + if (flag > 0) { + return angle; + } + if (flag < 0) { + return -1 * angle; + } + return 0.0D; + } + + + private Vector2D getVector(double fromX, double fromY, double toX, + double toY) { + return (new Point2D(toX, toY)).minus(new Point2D(fromX, fromY)); + } + + + private Vector2D scaleClear(Vector2D vector) { + return vector.normalised().scale(this.clearDistance); + } + + + private Vector2D scaleBackClear(Vector2D vector) { + return vector.normalised().scale(-510); + } + + + private Set getMovePoints(Road road) { + Set points = this.movePointCache.get(road.getID()); + if (points == null) { + points = new HashSet<>(); + int[] apex = road.getApexList(); + for (int i = 0; i < apex.length; i += 2) { + for (int j = i + 2; j < apex.length; j += 2) { + double midX = (apex[i] + apex[j]) / 2; + double midY = (apex[i + 1] + apex[j + 1]) / 2; + if (this.isInside(midX, midY, apex)) { + points.add(new Point2D(midX, midY)); + } + } + } + for (Edge edge : road.getEdges()) { + double midX = (edge.getStartX() + edge.getEndX()) / 2; + double midY = (edge.getStartY() + edge.getEndY()) / 2; + points.remove(new Point2D(midX, midY)); + } + this.movePointCache.put(road.getID(), points); + } + return points; + } + + + private boolean needRest(Human agent) { + int hp = agent.getHP(); + int damage = agent.getDamage(); + if (damage == 0 || hp == 0) { + return false; + } + int activeTime = (hp / damage) + ((hp % damage) != 0 ? 1 : 0); + if (this.kernelTime == -1) { + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + } + return damage >= this.thresholdRest + || (activeTime + this.agentInfo.getTime()) < this.kernelTime; + } + + + private Action calcRest(Human human, PathPlanning pathPlanning, + Collection targets) { + EntityID position = human.getPosition(); + Collection refuges = this.worldInfo + .getEntityIDsOfType(StandardEntityURN.REFUGE); + int currentSize = refuges.size(); + if (refuges.contains(position)) { + return new ActionRest(); + } + List firstResult = null; + while (refuges.size() > 0) { + pathPlanning.setFrom(position); + pathPlanning.setDestination(refuges); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + if (firstResult == null) { + firstResult = new ArrayList<>(path); + if (targets == null || targets.isEmpty()) { + break; + } + } + EntityID refugeID = path.get(path.size() - 1); + pathPlanning.setFrom(refugeID); + pathPlanning.setDestination(targets); + List fromRefugeToTarget = pathPlanning.calc().getResult(); + if (fromRefugeToTarget != null && fromRefugeToTarget.size() > 0) { + return new ActionMove(path); + } + refuges.remove(refugeID); + // remove failed + if (currentSize == refuges.size()) { + break; + } + currentSize = refuges.size(); + } else { + break; + } + } + return firstResult != null ? new ActionMove(firstResult) : null; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireFighting.java b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireFighting.java new file mode 100644 index 00000000..f5ae30c8 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireFighting.java @@ -0,0 +1,351 @@ +package adf_core_python_core_python.impl.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.action.common.ActionMove; +import adf.core.agent.action.common.ActionRest; +import adf.core.agent.action.fire.ActionExtinguish; +import adf.core.agent.action.fire.ActionRefill; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +import static rescuecore2.standard.entities.StandardEntityURN.HYDRANT; +import static rescuecore2.standard.entities.StandardEntityURN.REFUGE; + +public class DefaultExtActionFireFighting extends ExtAction { + + private PathPlanning pathPlanning; + + private int maxExtinguishDistance; + private int maxExtinguishPower; + private int thresholdRest; + private int kernelTime; + private int refillCompleted; + private int refillRequest; + private boolean refillFlag; + + private EntityID target; + + public DefaultExtActionFireFighting(AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, ModuleManager moduleManager, DevelopData developData) { + super(agentInfo, worldInfo, scenarioInfo, moduleManager, developData); + this.maxExtinguishDistance = scenarioInfo.getFireExtinguishMaxDistance(); + this.maxExtinguishPower = scenarioInfo.getFireExtinguishMaxSum(); + this.thresholdRest = developData.getInteger( + "adf.impl.extaction.DefaultExtActionFireFighting.rest", 100); + int maxWater = scenarioInfo.getFireTankMaximum(); + this.refillCompleted = (maxWater / 10) * developData.getInteger( + "adf.impl.extaction.DefaultExtActionFireFighting.refill.completed", 10); + this.refillRequest = this.maxExtinguishPower * developData.getInteger( + "adf.impl.extaction.DefaultExtActionFireFighting.refill.request", 1); + this.refillFlag = false; + + this.target = null; + + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultExtActionFireFighting.PathPlanning", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + } + + + @Override + public ExtAction precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.pathPlanning.precompute(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.pathPlanning.resume(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.pathPlanning.preparate(); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.pathPlanning.updateInfo(messageManager); + return this; + } + + + @Override + public ExtAction setTarget(EntityID target) { + this.target = null; + if (target != null) { + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity instanceof Building) { + this.target = target; + } + } + return this; + } + + + @Override + public ExtAction calc() { + this.result = null; + FireBrigade agent = (FireBrigade) this.agentInfo.me(); + + this.refillFlag = this.needRefill(agent, this.refillFlag); + if (this.refillFlag) { + this.result = this.calcRefill(agent, this.pathPlanning, this.target); + if (this.result != null) { + return this; + } + } + + if (this.needRest(agent)) { + this.result = this.calcRefugeAction(agent, this.pathPlanning, this.target, + false); + if (this.result != null) { + return this; + } + } + + if (this.target == null) { + return this; + } + this.result = this.calcExtinguish(agent, this.pathPlanning, this.target); + return this; + } + + + private Action calcExtinguish(FireBrigade agent, PathPlanning pathPlanning, + EntityID target) { + EntityID agentPosition = agent.getPosition(); + StandardEntity positionEntity = Objects + .requireNonNull(this.worldInfo.getPosition(agent)); + if (StandardEntityURN.REFUGE == positionEntity.getStandardURN()) { + Action action = this.getMoveAction(pathPlanning, agentPosition, target); + if (action != null) { + return action; + } + } + + List neighbourBuilding = new ArrayList<>(); + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity instanceof Building) { + if (this.worldInfo.getDistance(positionEntity, + entity) < this.maxExtinguishDistance) { + neighbourBuilding.add(entity); + } + } + + if (neighbourBuilding.size() > 0) { + neighbourBuilding.sort(new DistanceSorter(this.worldInfo, agent)); + return new ActionExtinguish(neighbourBuilding.get(0).getID(), + this.maxExtinguishPower); + } + return this.getMoveAction(pathPlanning, agentPosition, target); + } + + + private Action getMoveAction(PathPlanning pathPlanning, EntityID from, + EntityID target) { + pathPlanning.setFrom(from); + pathPlanning.setDestination(target); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + StandardEntity entity = this.worldInfo + .getEntity(path.get(path.size() - 1)); + if (entity instanceof Building) { + if (entity.getStandardURN() != StandardEntityURN.REFUGE) { + path.remove(path.size() - 1); + } + } + return new ActionMove(path); + } + return null; + } + + + private boolean needRefill(FireBrigade agent, boolean refillFlag) { + if (refillFlag) { + StandardEntityURN positionURN = Objects + .requireNonNull(this.worldInfo.getPosition(agent)).getStandardURN(); + return !(positionURN == REFUGE || positionURN == HYDRANT) + || agent.getWater() < this.refillCompleted; + } + return agent.getWater() <= this.refillRequest; + } + + + private boolean needRest(Human agent) { + int hp = agent.getHP(); + int damage = agent.getDamage(); + if (hp == 0 || damage == 0) { + return false; + } + int activeTime = (hp / damage) + ((hp % damage) != 0 ? 1 : 0); + if (this.kernelTime == -1) { + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + } + return damage >= this.thresholdRest + || (activeTime + this.agentInfo.getTime()) < this.kernelTime; + } + + + private Action calcRefill(FireBrigade agent, PathPlanning pathPlanning, + EntityID target) { + StandardEntityURN positionURN = Objects + .requireNonNull(this.worldInfo.getPosition(agent)).getStandardURN(); + if (positionURN == REFUGE) { + return new ActionRefill(); + } + Action action = this.calcRefugeAction(agent, pathPlanning, target, true); + if (action != null) { + return action; + } + action = this.calcHydrantAction(agent, pathPlanning, target); + if (action != null) { + if (positionURN == HYDRANT + && action.getClass().equals(ActionMove.class)) { + pathPlanning.setFrom(agent.getPosition()); + pathPlanning.setDestination(target); + double currentDistance = pathPlanning.calc().getDistance(); + List path = ((ActionMove) action).getPath(); + pathPlanning.setFrom(path.get(path.size() - 1)); + pathPlanning.setDestination(target); + double newHydrantDistance = pathPlanning.calc().getDistance(); + if (currentDistance <= newHydrantDistance) { + return new ActionRefill(); + } + } + return action; + } + return null; + } + + + private Action calcRefugeAction(Human human, PathPlanning pathPlanning, + EntityID target, boolean isRefill) { + return this.calcSupplyAction(human, pathPlanning, + this.worldInfo.getEntityIDsOfType(StandardEntityURN.REFUGE), target, + isRefill); + } + + + private Action calcHydrantAction(Human human, PathPlanning pathPlanning, + EntityID target) { + Collection hydrants = this.worldInfo.getEntityIDsOfType(HYDRANT); + hydrants.remove(human.getPosition()); + return this.calcSupplyAction(human, pathPlanning, hydrants, target, true); + } + + + private Action calcSupplyAction(Human human, PathPlanning pathPlanning, + Collection supplyPositions, EntityID target, boolean isRefill) { + EntityID position = human.getPosition(); + int size = supplyPositions.size(); + if (supplyPositions.contains(position)) { + return isRefill ? new ActionRefill() : new ActionRest(); + } + List firstResult = null; + while (supplyPositions.size() > 0) { + pathPlanning.setFrom(position); + pathPlanning.setDestination(supplyPositions); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + if (firstResult == null) { + firstResult = new ArrayList<>(path); + if (target == null) { + break; + } + } + EntityID supplyPositionID = path.get(path.size() - 1); + pathPlanning.setFrom(supplyPositionID); + pathPlanning.setDestination(target); + List fromRefugeToTarget = pathPlanning.calc().getResult(); + if (fromRefugeToTarget != null && fromRefugeToTarget.size() > 0) { + return new ActionMove(path); + } + supplyPositions.remove(supplyPositionID); + // remove failed + if (size == supplyPositions.size()) { + break; + } + size = supplyPositions.size(); + } else { + break; + } + } + return firstResult != null ? new ActionMove(firstResult) : null; + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireRescue.java b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireRescue.java new file mode 100644 index 00000000..05f4bb7b --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionFireRescue.java @@ -0,0 +1,227 @@ +package adf_core_python.impl.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.action.ambulance.ActionRescue; +import adf.core.agent.action.common.ActionMove; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.List; + +import static rescuecore2.standard.entities.StandardEntityURN.BLOCKADE; + +public class DefaultExtActionFireRescue extends ExtAction { + + private PathPlanning pathPlanning; + + private int thresholdRest; + private int kernelTime; + + private EntityID target; + + public DefaultExtActionFireRescue(AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, ModuleManager moduleManager, DevelopData developData) { + super(agentInfo, worldInfo, scenarioInfo, moduleManager, developData); + this.target = null; + this.thresholdRest = developData + .getInteger("adf.impl.extaction.DefaultExtActionFireRescue.rest", 100); + + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultExtActionFireRescue.PathPlanning", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + } + + + public ExtAction precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.pathPlanning.precompute(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.pathPlanning.resume(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.pathPlanning.preparate(); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.pathPlanning.updateInfo(messageManager); + return this; + } + + + @Override + public ExtAction setTarget(EntityID target) { + this.target = null; + if (target != null) { + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity instanceof Human || entity instanceof Area) { + this.target = target; + return this; + } + } + return this; + } + + + @Override + public ExtAction calc() { + this.result = null; + FireBrigade agent = (FireBrigade) this.agentInfo.me(); + + if (this.needRest(agent)) { + EntityID areaID = this.convertArea(this.target); + ArrayList targets = new ArrayList<>(); + if (areaID != null) { + targets.add(areaID); + } + } + if (this.target != null) { + this.result = this.calcRescue(agent, this.pathPlanning, this.target); + } + return this; + } + + + private Action calcRescue(FireBrigade agent, PathPlanning pathPlanning, + EntityID targetID) { + StandardEntity targetEntity = this.worldInfo.getEntity(targetID); + if (targetEntity == null) { + return null; + } + EntityID agentPosition = agent.getPosition(); + if (targetEntity instanceof Human) { + Human human = (Human) targetEntity; + if (!human.isPositionDefined()) { + return null; + } + if (human.isHPDefined() && human.getHP() == 0) { + return null; + } + EntityID targetPosition = human.getPosition(); + if (agentPosition.getValue() == targetPosition.getValue()) { + if (human.isBuriednessDefined() && human.getBuriedness() > 0) { + return new ActionRescue(human); + } + } else { + List path = pathPlanning.getResult(agentPosition, + targetPosition); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + return null; + } + if (targetEntity.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) targetEntity; + if (blockade.isPositionDefined()) { + targetEntity = this.worldInfo.getEntity(blockade.getPosition()); + } + } + if (targetEntity instanceof Area) { + List path = pathPlanning.getResult(agentPosition, + targetEntity.getID()); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + return null; + } + + + private boolean needRest(Human agent) { + int hp = agent.getHP(); + int damage = agent.getDamage(); + if (hp == 0 || damage == 0) { + return false; + } + int activeTime = (hp / damage) + ((hp % damage) != 0 ? 1 : 0); + if (this.kernelTime == -1) { + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + } + return damage >= this.thresholdRest + || (activeTime + this.agentInfo.getTime()) < this.kernelTime; + } + + + private EntityID convertArea(EntityID targetID) { + StandardEntity entity = this.worldInfo.getEntity(targetID); + if (entity == null) { + return null; + } + if (entity instanceof Human) { + Human human = (Human) entity; + if (human.isPositionDefined()) { + EntityID position = human.getPosition(); + if (this.worldInfo.getEntity(position) instanceof Area) { + return position; + } + } + } else if (entity instanceof Area) { + return targetID; + } else if (entity.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) entity; + if (blockade.isPositionDefined()) { + return blockade.getPosition(); + } + } + return null; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionMove.java b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionMove.java new file mode 100644 index 00000000..4f79cab3 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionMove.java @@ -0,0 +1,210 @@ +package adf_core_python.impl.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.action.common.ActionMove; +import adf.core.agent.action.common.ActionRest; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class DefaultExtActionMove extends ExtAction { + + private PathPlanning pathPlanning; + + private int thresholdRest; + private int kernelTime; + + private EntityID target; + + public DefaultExtActionMove(AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, ModuleManager moduleManager, DevelopData developData) { + super(agentInfo, worldInfo, scenarioInfo, moduleManager, developData); + this.target = null; + this.thresholdRest = developData + .getInteger("adf.impl.extaction.DefaultExtActionMove.rest", 100); + + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultExtActionMove.PathPlanning", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + } + + + @Override + public ExtAction precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.pathPlanning.precompute(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.pathPlanning.resume(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.pathPlanning.preparate(); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + @Override + public ExtAction updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.pathPlanning.updateInfo(messageManager); + return this; + } + + + @Override + public ExtAction setTarget(EntityID target) { + this.target = null; + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity != null) { + if (entity.getStandardURN().equals(StandardEntityURN.BLOCKADE)) { + entity = this.worldInfo.getEntity(((Blockade) entity).getPosition()); + } else if (entity instanceof Human) { + entity = this.worldInfo.getPosition((Human) entity); + } + if (entity != null && entity instanceof Area) { + this.target = entity.getID(); + } + } + return this; + } + + + @Override + public ExtAction calc() { + this.result = null; + Human agent = (Human) this.agentInfo.me(); + + if (this.needRest(agent)) { + this.result = this.calcRest(agent, this.pathPlanning, this.target); + if (this.result != null) { + return this; + } + } + if (this.target == null) { + return this; + } + this.pathPlanning.setFrom(agent.getPosition()); + this.pathPlanning.setDestination(this.target); + List path = this.pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + this.result = new ActionMove(path); + } + return this; + } + + + private boolean needRest(Human agent) { + int hp = agent.getHP(); + int damage = agent.getDamage(); + if (hp == 0 || damage == 0) { + return false; + } + int activeTime = (hp / damage) + ((hp % damage) != 0 ? 1 : 0); + if (this.kernelTime == -1) { + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + } + return damage >= this.thresholdRest + || (activeTime + this.agentInfo.getTime()) < this.kernelTime; + } + + + private Action calcRest(Human human, PathPlanning pathPlanning, + EntityID target) { + EntityID position = human.getPosition(); + Collection refuges = this.worldInfo + .getEntityIDsOfType(StandardEntityURN.REFUGE); + int currentSize = refuges.size(); + if (refuges.contains(position)) { + return new ActionRest(); + } + List firstResult = null; + while (refuges.size() > 0) { + pathPlanning.setFrom(position); + pathPlanning.setDestination(refuges); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + if (firstResult == null) { + firstResult = new ArrayList<>(path); + if (target == null) { + break; + } + } + EntityID refugeID = path.get(path.size() - 1); + pathPlanning.setFrom(refugeID); + pathPlanning.setDestination(target); + List fromRefugeToTarget = pathPlanning.calc().getResult(); + if (fromRefugeToTarget != null && fromRefugeToTarget.size() > 0) { + return new ActionMove(path); + } + refuges.remove(refugeID); + // remove failed + if (currentSize == refuges.size()) { + break; + } + currentSize = refuges.size(); + } else { + break; + } + } + return firstResult != null ? new ActionMove(firstResult) : null; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionTransport.java b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionTransport.java new file mode 100644 index 00000000..3d134e9d --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/extaction/DefaultExtActionTransport.java @@ -0,0 +1,348 @@ +package adf_core_python.impl.extaction; + +import adf.core.agent.action.Action; +import adf.core.agent.action.ambulance.ActionLoad; +import adf.core.agent.action.ambulance.ActionUnload; +import adf.core.agent.action.common.ActionMove; +import adf.core.agent.action.common.ActionRest; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.extaction.ExtAction; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import com.google.common.collect.Lists; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static rescuecore2.standard.entities.StandardEntityURN.*; + +public class DefaultExtActionTransport extends ExtAction { + + private PathPlanning pathPlanning; + + private int thresholdRest; + private int kernelTime; + + private EntityID target; + + public DefaultExtActionTransport(AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, ModuleManager moduleManager, DevelopData developData) { + super(agentInfo, worldInfo, scenarioInfo, moduleManager, developData); + this.target = null; + this.thresholdRest = developData + .getInteger("adf.impl.extaction.DefaultExtActionTransport.rest", 100); + + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultExtActionTransport.PathPlanning", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + } + + + public ExtAction precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.pathPlanning.precompute(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.pathPlanning.resume(precomputeData); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.pathPlanning.preparate(); + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + return this; + } + + + public ExtAction updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.pathPlanning.updateInfo(messageManager); + return this; + } + + + @Override + public ExtAction setTarget(EntityID target) { + this.target = null; + if (target != null) { + StandardEntity entity = this.worldInfo.getEntity(target); + if (entity instanceof Human || entity instanceof Area) { + this.target = target; + return this; + } + } + return this; + } + + + @Override + public ExtAction calc() { + this.result = null; + AmbulanceTeam agent = (AmbulanceTeam) this.agentInfo.me(); + Human transportHuman = this.agentInfo.someoneOnBoard(); + + if (transportHuman != null) { + this.result = this.calcUnload(agent, this.pathPlanning, transportHuman, + this.target); + if (this.result != null) { + return this; + } + } + if (this.needRest(agent)) { + EntityID areaID = this.convertArea(this.target); + ArrayList targets = new ArrayList<>(); + if (areaID != null) { + targets.add(areaID); + } + this.result = this.calcRefugeAction(agent, this.pathPlanning, targets, + false); + if (this.result != null) { + return this; + } + } + if (this.target != null) { + this.result = this.calcRescue(agent, this.pathPlanning, this.target); + } + return this; + } + + + private Action calcRescue(AmbulanceTeam agent, PathPlanning pathPlanning, + EntityID targetID) { + StandardEntity targetEntity = this.worldInfo.getEntity(targetID); + if (targetEntity == null) { + return null; + } + EntityID agentPosition = agent.getPosition(); + if (targetEntity instanceof Human) { + Human human = (Human) targetEntity; + if (!human.isPositionDefined()) { + return null; + } + if (human.isHPDefined() && human.getHP() == 0) { + return null; + } + EntityID targetPosition = human.getPosition(); + if (agentPosition.getValue() == targetPosition.getValue()) { + if ((human.getStandardURN() == CIVILIAN) && + (!human.isBuriednessDefined() || (human.isBuriednessDefined() && (human.getBuriedness() == 0)))) { + return new ActionLoad(human.getID()); + } + } else { + List path = pathPlanning.getResult(agentPosition, + targetPosition); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + return null; + } + if (targetEntity.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) targetEntity; + if (blockade.isPositionDefined()) { + targetEntity = this.worldInfo.getEntity(blockade.getPosition()); + } + } + if (targetEntity instanceof Area) { + List path = pathPlanning.getResult(agentPosition, + targetEntity.getID()); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + return null; + } + + + private Action calcUnload(AmbulanceTeam agent, PathPlanning pathPlanning, + Human transportHuman, EntityID targetID) { + if (transportHuman == null) { + return null; + } + if (transportHuman.isHPDefined() && transportHuman.getHP() == 0) { + return new ActionUnload(); + } + EntityID agentPosition = agent.getPosition(); + if (targetID == null + || transportHuman.getID().getValue() == targetID.getValue()) { + StandardEntity position = this.worldInfo.getEntity(agentPosition); + if (position != null && position.getStandardURN() == REFUGE) { + return new ActionUnload(); + } else { + pathPlanning.setFrom(agentPosition); + pathPlanning.setDestination(this.worldInfo.getEntityIDsOfType(REFUGE)); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + } + if (targetID == null) { + return null; + } + StandardEntity targetEntity = this.worldInfo.getEntity(targetID); + if (targetEntity != null && targetEntity.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) targetEntity; + if (blockade.isPositionDefined()) { + targetEntity = this.worldInfo.getEntity(blockade.getPosition()); + } + } + if (targetEntity instanceof Area) { + if (agentPosition.getValue() == targetID.getValue()) { + return new ActionUnload(); + } else { + pathPlanning.setFrom(agentPosition); + pathPlanning.setDestination(targetID); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + } else if (targetEntity instanceof Human) { + Human human = (Human) targetEntity; + if (human.isPositionDefined()) { + return calcRefugeAction(agent, pathPlanning, + Lists.newArrayList(human.getPosition()), true); + } + pathPlanning.setFrom(agentPosition); + pathPlanning.setDestination(this.worldInfo.getEntityIDsOfType(REFUGE)); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + return new ActionMove(path); + } + } + return null; + } + + + private boolean needRest(Human agent) { + int hp = agent.getHP(); + int damage = agent.getDamage(); + if (hp == 0 || damage == 0) { + return false; + } + int activeTime = (hp / damage) + ((hp % damage) != 0 ? 1 : 0); + if (this.kernelTime == -1) { + try { + this.kernelTime = this.scenarioInfo.getKernelTimesteps(); + } catch (NoSuchConfigOptionException e) { + this.kernelTime = -1; + } + } + return damage >= this.thresholdRest + || (activeTime + this.agentInfo.getTime()) < this.kernelTime; + } + + + private EntityID convertArea(EntityID targetID) { + StandardEntity entity = this.worldInfo.getEntity(targetID); + if (entity == null) { + return null; + } + if (entity instanceof Human) { + Human human = (Human) entity; + if (human.isPositionDefined()) { + EntityID position = human.getPosition(); + if (this.worldInfo.getEntity(position) instanceof Area) { + return position; + } + } + } else if (entity instanceof Area) { + return targetID; + } else if (entity.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) entity; + if (blockade.isPositionDefined()) { + return blockade.getPosition(); + } + } + return null; + } + + + private Action calcRefugeAction(Human human, PathPlanning pathPlanning, + Collection targets, boolean isUnload) { + EntityID position = human.getPosition(); + Collection refuges = this.worldInfo + .getEntityIDsOfType(StandardEntityURN.REFUGE); + int size = refuges.size(); + if (refuges.contains(position)) { + return isUnload ? new ActionUnload() : new ActionRest(); + } + List firstResult = null; + while (refuges.size() > 0) { + pathPlanning.setFrom(position); + pathPlanning.setDestination(refuges); + List path = pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + if (firstResult == null) { + firstResult = new ArrayList<>(path); + if (targets == null || targets.isEmpty()) { + break; + } + } + EntityID refugeID = path.get(path.size() - 1); + pathPlanning.setFrom(refugeID); + pathPlanning.setDestination(targets); + List fromRefugeToTarget = pathPlanning.calc().getResult(); + if (fromRefugeToTarget != null && fromRefugeToTarget.size() > 0) { + return new ActionMove(path); + } + refuges.remove(refugeID); + // remove failed + if (size == refuges.size()) { + break; + } + size = refuges.size(); + } else { + break; + } + } + return firstResult != null ? new ActionMove(firstResult) : null; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/algorithm/AStarPathPlanning.java b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/AStarPathPlanning.java new file mode 100644 index 00000000..d8d8fba3 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/AStarPathPlanning.java @@ -0,0 +1,198 @@ +package adf_core_python.impl.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.standard.entities.Area; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +public class AStarPathPlanning extends PathPlanning { + + private Map> graph; + + private EntityID from; + private Collection targets; + private List result; + + public AStarPathPlanning(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.init(); + } + + + private void init() { + Map> neighbours = new LazyMap>() { + + @Override + public Set createValue() { + return new HashSet<>(); + } + }; + for (Entity next : this.worldInfo) { + if (next instanceof Area) { + Collection areaNeighbours = ((Area) next).getNeighbours(); + neighbours.get(next.getID()).addAll(areaNeighbours); + } + } + this.graph = neighbours; + } + + + @Override + public List getResult() { + return this.result; + } + + + @Override + public PathPlanning setFrom(EntityID id) { + this.from = id; + return this; + } + + + @Override + public PathPlanning setDestination(Collection targets) { + this.targets = targets; + return this; + } + + + @Override + public PathPlanning precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public PathPlanning resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public PathPlanning preparate() { + super.preparate(); + return this; + } + + + @Override + public PathPlanning calc() { + // 1 + List open = new LinkedList<>(); + List close = new LinkedList<>(); + Map nodeMap = new HashMap<>(); + + // 3 + open.add(this.from); + nodeMap.put(this.from, new Node(null, this.from)); + close.clear(); + + while (true) { + // 4 + if (open.size() < 0) { + this.result = null; + return this; + } + + // 5 + Node n = null; + for (EntityID id : open) { + Node node = nodeMap.get(id); + + if (n == null) { + n = node; + } else if (node.estimate() < n.estimate()) { + n = node; + } + } + + // 6 + if (targets.contains(n.getID())) { + // 9 + List path = new LinkedList<>(); + while (n != null) { + path.add(0, n.getID()); + n = nodeMap.get(n.getParent()); + } + + this.result = path; + return this; + } + open.remove(n.getID()); + close.add(n.getID()); + + // 7 + Collection neighbours = this.graph.get(n.getID()); + for (EntityID neighbour : neighbours) { + Node m = new Node(n, neighbour); + + if (!open.contains(neighbour) && !close.contains(neighbour)) { + open.add(m.getID()); + nodeMap.put(neighbour, m); + } else if (open.contains(neighbour) + && m.estimate() < nodeMap.get(neighbour).estimate()) { + nodeMap.put(neighbour, m); + } else if (!close.contains(neighbour) + && m.estimate() < nodeMap.get(neighbour).estimate()) { + nodeMap.put(neighbour, m); + } + } + } + } + + private class Node { + + EntityID id; + EntityID parent; + + double cost; + double heuristic; + + public Node(Node from, EntityID id) { + this.id = id; + + if (from == null) { + this.cost = 0; + } else { + this.parent = from.getID(); + this.cost = from.getCost() + worldInfo.getDistance(from.getID(), id); + } + + this.heuristic = worldInfo.getDistance(id, + targets.toArray(new EntityID[targets.size()])[0]); + } + + + public EntityID getID() { + return id; + } + + + public double getCost() { + return cost; + } + + + public double estimate() { + return cost + heuristic; + } + + + public EntityID getParent() { + return this.parent; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/algorithm/DijkstraPathPlanning.java b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/DijkstraPathPlanning.java new file mode 100644 index 00000000..c43aecd1 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/DijkstraPathPlanning.java @@ -0,0 +1,154 @@ +package adf_core_python.impl.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.standard.entities.Area; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +public class DijkstraPathPlanning extends PathPlanning { + + private Map> graph; + + private EntityID from; + private Collection targets; + private List result; + + public DijkstraPathPlanning(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.init(); + } + + + private void init() { + Map> neighbours = new LazyMap>() { + + @Override + public Set createValue() { + return new HashSet<>(); + } + }; + for (Entity next : this.worldInfo) { + if (next instanceof Area) { + Collection areaNeighbours = ((Area) next).getNeighbours(); + neighbours.get(next.getID()).addAll(areaNeighbours); + } + } + this.graph = neighbours; + } + + + @Override + public List getResult() { + return this.result; + } + + + @Override + public PathPlanning setFrom(EntityID id) { + this.from = id; + return this; + } + + + @Override + public PathPlanning setDestination(Collection targets) { + this.targets = targets; + return this; + } + + + @Override + public PathPlanning updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + return this; + } + + + @Override + public PathPlanning precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + return this; + } + + + @Override + public PathPlanning resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + return this; + } + + + @Override + public PathPlanning preparate() { + super.preparate(); + return this; + } + + + @Override + public PathPlanning calc() { + List open = new LinkedList<>(); + Map ancestors = new HashMap<>(); + open.add(this.from); + EntityID next; + boolean found = false; + ancestors.put(this.from, this.from); + do { + next = open.remove(0); + if (isGoal(next, targets)) { + found = true; + break; + } + Collection neighbours = graph.get(next); + if (neighbours.isEmpty()) { + continue; + } + for (EntityID neighbour : neighbours) { + if (isGoal(neighbour, targets)) { + ancestors.put(neighbour, next); + next = neighbour; + found = true; + break; + } else { + if (!ancestors.containsKey(neighbour)) { + open.add(neighbour); + ancestors.put(neighbour, next); + } + } + } + } while (!found && !open.isEmpty()); + if (!found) { + // No path + this.result = null; + } + // Walk back from goal to this.from + EntityID current = next; + LinkedList path = new LinkedList<>(); + do { + path.add(0, current); + current = ancestors.get(current); + if (current == null) { + throw new RuntimeException( + "Found a node with no ancestor! Something is broken."); + } + } while (current != this.from); + this.result = path; + return this; + } + + + private boolean isGoal(EntityID e, Collection test) { + return test.contains(e); + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/algorithm/FireClustering.java b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/FireClustering.java new file mode 100644 index 00000000..b00053ed --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/FireClustering.java @@ -0,0 +1,261 @@ +package adf_core_python.impl.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.algorithm.DynamicClustering; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +public class FireClustering extends DynamicClustering { + + List> clusterList = new LinkedList<>(); + private int groupingDistance; + + public FireClustering(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.groupingDistance = developData.getInteger( + "adf.impl.module.algorithm.FireClustering.groupingDistance", 30); + } + + + /** + * calculation phase; update cluster + * + * @return own instance for method chaining + */ + @Override + public Clustering calc() { + for (EntityID changed : worldInfo.getChanged().getChangedEntities()) { + StandardEntity changedEntity = worldInfo.getEntity(changed); + if (changedEntity.getStandardURN().equals(StandardEntityURN.BUILDING)) { // changedEntity(cE) + // is + // a + // building + Building changedBuilding = (Building) changedEntity; + if (this.getClusterIndex(changedEntity) < 0) { // cE is not contained in + // cluster + if (isBurning(changedBuilding)) { // cE is burning building + ArrayList< + EntityID> hostClusterPropertyEntityIDs = new ArrayList<>(); + + // search host cluster + for (List cluster : this.clusterList) { + for (StandardEntity entity : cluster) { + if (worldInfo.getDistance(entity, + changedBuilding) <= groupingDistance) { + hostClusterPropertyEntityIDs.add(entity.getID()); + break; + } + } + } + + if (hostClusterPropertyEntityIDs.size() == 0) { // there is not host + // cluster : form + // new cluster + List cluster = new ArrayList<>(); + clusterList.add(cluster); + cluster.add(changedBuilding); + } else if (hostClusterPropertyEntityIDs.size() == 1) { // there is + // one host + // cluster : + // add + // building + // to the + // cluster + int hostIndex = this + .getClusterIndex(hostClusterPropertyEntityIDs.get(0)); + clusterList.get(hostIndex).add(changedBuilding); + } else { // there are multiple host clusters : add building to the + // cluster & combine clusters + int hostIndex = this + .getClusterIndex(hostClusterPropertyEntityIDs.get(0)); + List hostCluster = clusterList.get(hostIndex); + hostCluster.add(changedBuilding); + for (int index = 1; index < hostClusterPropertyEntityIDs + .size(); index++) { + int tergetClusterIndex = this + .getClusterIndex(hostClusterPropertyEntityIDs.get(index)); + hostCluster.addAll(clusterList.get(tergetClusterIndex)); + clusterList.remove(tergetClusterIndex); + } + } + } + } else { // cE is contained in cluster + if (!(isBurning(changedBuilding))) { // cE is not burning building + int hostClusterIndex = this.getClusterIndex(changedBuilding); + List< + StandardEntity> hostCluster = clusterList.get(hostClusterIndex); + + hostCluster.remove(changedBuilding); + + if (hostCluster.isEmpty()) { // host cluster is empty + clusterList.remove(hostClusterIndex); + } else { + // update cluster + List relatedBuilding = new ArrayList<>(); + relatedBuilding.addAll(hostCluster); + hostCluster.clear(); + + int clusterCount = 0; + while (!(relatedBuilding.isEmpty())) { + if ((clusterCount++) > 0) { + List cluster = new ArrayList<>(); + clusterList.add(cluster); + hostCluster = cluster; + } + + List openedBuilding = new LinkedList<>(); + openedBuilding.add(relatedBuilding.get(0)); + hostCluster.add(relatedBuilding.get(0)); + relatedBuilding.remove(0); + + while (!(openedBuilding.isEmpty())) { + for (StandardEntity entity : relatedBuilding) { + if (worldInfo.getDistance(openedBuilding.get(0), + entity) <= groupingDistance) { + openedBuilding.add(entity); + hostCluster.add(entity); + } + } + openedBuilding.remove(0); + relatedBuilding.removeAll(openedBuilding); + } + } + } + } + } + } + } + return this; + } + + + @Override + public Clustering updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() > 1) { + return this; + } + + this.calc(); // invoke calc() + + this.debugStdOut("Cluster : " + clusterList.size()); + + return this; + } + + + @Override + public Clustering precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() > 1) { + return this; + } + return this; + } + + + @Override + public Clustering resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() > 1) { + return this; + } + return this; + } + + + @Override + public Clustering preparate() { + super.preparate(); + if (this.getCountPreparate() > 1) { + return this; + } + return this; + } + + + @Override + public int getClusterNumber() { + return clusterList.size(); + } + + + @Override + public int getClusterIndex(StandardEntity standardEntity) { + for (int index = 0; index < clusterList.size(); index++) { + if (clusterList.get(index).contains(standardEntity)) { + return index; + } + } + return -1; + } + + + @Override + public int getClusterIndex(EntityID entityID) { + return getClusterIndex(worldInfo.getEntity(entityID)); + } + + + @Override + public Collection getClusterEntities(int i) { + return clusterList.get(i); + } + + + @Override + public Collection getClusterEntityIDs(int i) { + ArrayList list = new ArrayList<>(); + for (StandardEntity entity : getClusterEntities(i)) { + list.add(entity.getID()); + } + return list; + } + + + /** + * classify burning building + * + * @param building target building + * @return is building burning + */ + private boolean isBurning(Building building) { + if (building.isFierynessDefined()) { + switch (building.getFieryness()) { + case 1: + case 2: + case 3: + return true; + default: + return false; + } + } + return false; + } + + + /** + * output text with class name to STDOUT when debug-mode. + * + * @param text output text + */ + private void debugStdOut(String text) { + if (scenarioInfo.isDebugMode()) { + System.out.println("[" + this.getClass().getSimpleName() + "] " + text); + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/algorithm/KMeansClustering.java b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/KMeansClustering.java new file mode 100644 index 00000000..1dae9754 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/algorithm/KMeansClustering.java @@ -0,0 +1,641 @@ +package adf_core_python.impl.module.algorithm; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.algorithm.StaticClustering; +import rescuecore2.misc.Pair; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +public class KMeansClustering extends StaticClustering { + + private static final String KEY_CLUSTER_SIZE = "clustering.size"; + private static final String KEY_CLUSTER_CENTER = "clustering.centers"; + private static final String KEY_CLUSTER_ENTITY = "clustering.entities."; + private static final String KEY_ASSIGN_AGENT = "clustering.assign"; + + private int repeatPrecompute; + private int repeatPreparate; + + private Collection entities; + + private List centerList; + private List centerIDs; + private Map> clusterEntitiesList; + private List> clusterEntityIDsList; + + private int clusterSize; + + private boolean assignAgentsFlag; + + private Map> shortestPathGraph; + + public KMeansClustering(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.repeatPrecompute = developData.getInteger( + "adf.impl.module.algorithm.KMeansClustering.repeatPrecompute", 7); + this.repeatPreparate = developData.getInteger( + "adf.impl.module.algorithm.KMeansClustering.repeatPreparate", 30); + this.clusterSize = developData.getInteger( + "adf.impl.module.algorithm.KMeansClustering.clusterSize", 5); + if (agentInfo.me().getStandardURN() + .equals(StandardEntityURN.AMBULANCE_TEAM)) { + this.clusterSize = scenarioInfo.getScenarioAgentsAt(); + } else if (agentInfo.me().getStandardURN() + .equals(StandardEntityURN.FIRE_BRIGADE)) { + this.clusterSize = scenarioInfo.getScenarioAgentsFb(); + } else if (agentInfo.me().getStandardURN() + .equals(StandardEntityURN.POLICE_FORCE)) { + this.clusterSize = scenarioInfo.getScenarioAgentsPf(); + } + this.assignAgentsFlag = developData.getBoolean( + "adf.impl.module.algorithm.KMeansClustering.assignAgentsFlag", true); + this.clusterEntityIDsList = new ArrayList<>(); + this.centerIDs = new ArrayList<>(); + this.clusterEntitiesList = new HashMap<>(); + this.centerList = new ArrayList<>(); + this.entities = wi.getEntitiesOfType(StandardEntityURN.ROAD, + StandardEntityURN.HYDRANT, StandardEntityURN.BUILDING, + StandardEntityURN.REFUGE, StandardEntityURN.GAS_STATION, + StandardEntityURN.AMBULANCE_CENTRE, StandardEntityURN.FIRE_STATION, + StandardEntityURN.POLICE_OFFICE); + } + + + @Override + public Clustering updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + this.centerList.clear(); + this.clusterEntitiesList.clear(); + return this; + } + + + @Override + public Clustering precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + this.calcPathBased(this.repeatPrecompute); + this.entities = null; + // write + precomputeData.setInteger(KEY_CLUSTER_SIZE, this.clusterSize); + precomputeData.setEntityIDList(KEY_CLUSTER_CENTER, this.centerIDs); + for (int i = 0; i < this.clusterSize; i++) { + precomputeData.setEntityIDList(KEY_CLUSTER_ENTITY + i, + this.clusterEntityIDsList.get(i)); + } + precomputeData.setBoolean(KEY_ASSIGN_AGENT, this.assignAgentsFlag); + return this; + } + + + @Override + public Clustering resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.entities = null; + // read + this.clusterSize = precomputeData.getInteger(KEY_CLUSTER_SIZE); + this.centerIDs = new ArrayList<>( + precomputeData.getEntityIDList(KEY_CLUSTER_CENTER)); + this.clusterEntityIDsList = new ArrayList<>(this.clusterSize); + for (int i = 0; i < this.clusterSize; i++) { + this.clusterEntityIDsList.add(i, + precomputeData.getEntityIDList(KEY_CLUSTER_ENTITY + i)); + } + this.assignAgentsFlag = precomputeData.getBoolean(KEY_ASSIGN_AGENT); + return this; + } + + + @Override + public Clustering preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.calcStandard(this.repeatPreparate); + this.entities = null; + return this; + } + + + @Override + public int getClusterNumber() { + // The number of clusters + return this.clusterSize; + } + + + @Override + public int getClusterIndex(StandardEntity entity) { + return this.getClusterIndex(entity.getID()); + } + + + @Override + public int getClusterIndex(EntityID id) { + for (int i = 0; i < this.clusterSize; i++) { + if (this.clusterEntityIDsList.get(i).contains(id)) { + return i; + } + } + return -1; + } + + + @Override + public Collection getClusterEntities(int index) { + List result = this.clusterEntitiesList.get(index); + if (result == null || result.isEmpty()) { + List list = this.clusterEntityIDsList.get(index); + result = new ArrayList<>(list.size()); + for (int i = 0; i < list.size(); i++) { + result.add(i, this.worldInfo.getEntity(list.get(i))); + } + this.clusterEntitiesList.put(index, result); + } + return result; + } + + + @Override + public Collection getClusterEntityIDs(int index) { + return this.clusterEntityIDsList.get(index); + } + + + @Override + public Clustering calc() { + return this; + } + + + private void calcStandard(int repeat) { + this.initShortestPath(this.worldInfo); + Random random = new Random(); + + List entityList = new ArrayList<>(this.entities); + this.centerList = new ArrayList<>(this.clusterSize); + this.clusterEntitiesList = new HashMap<>(this.clusterSize); + + // init list + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + this.centerList.add(index, entityList.get(0)); + } + System.out.println("[" + this.getClass().getSimpleName() + "] Cluster : " + + this.clusterSize); + // init center + for (int index = 0; index < this.clusterSize; index++) { + StandardEntity centerEntity; + do { + centerEntity = entityList + .get(Math.abs(random.nextInt()) % entityList.size()); + } while (this.centerList.contains(centerEntity)); + this.centerList.set(index, centerEntity); + } + // calc center + for (int i = 0; i < repeat; i++) { + this.clusterEntitiesList.clear(); + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + } + for (StandardEntity entity : entityList) { + StandardEntity tmp = this.getNearEntityByLine(this.worldInfo, + this.centerList, entity); + this.clusterEntitiesList.get(this.centerList.indexOf(tmp)).add(entity); + } + for (int index = 0; index < this.clusterSize; index++) { + int sumX = 0, sumY = 0; + for (StandardEntity entity : this.clusterEntitiesList.get(index)) { + Pair location = this.worldInfo.getLocation(entity); + sumX += location.first(); + sumY += location.second(); + } + int centerX = sumX / this.clusterEntitiesList.get(index).size(); + int centerY = sumY / this.clusterEntitiesList.get(index).size(); + StandardEntity center = this.getNearEntityByLine(this.worldInfo, + this.clusterEntitiesList.get(index), centerX, centerY); + if (center instanceof Area) { + this.centerList.set(index, center); + } else if (center instanceof Human) { + this.centerList.set(index, + this.worldInfo.getEntity(((Human) center).getPosition())); + } else if (center instanceof Blockade) { + this.centerList.set(index, + this.worldInfo.getEntity(((Blockade) center).getPosition())); + } + } + if (scenarioInfo.isDebugMode()) { + System.out.print("*"); + } + } + + if (scenarioInfo.isDebugMode()) { + System.out.println(); + } + + // set entity + this.clusterEntitiesList.clear(); + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + } + for (StandardEntity entity : entityList) { + StandardEntity tmp = this.getNearEntityByLine(this.worldInfo, + this.centerList, entity); + this.clusterEntitiesList.get(this.centerList.indexOf(tmp)).add(entity); + } + + // this.clusterEntitiesList.sort(comparing(List::size, reverseOrder())); + + if (this.assignAgentsFlag) { + List firebrigadeList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)); + List policeforceList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.POLICE_FORCE)); + List ambulanceteamList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.AMBULANCE_TEAM)); + + this.assignAgents(this.worldInfo, firebrigadeList); + this.assignAgents(this.worldInfo, policeforceList); + this.assignAgents(this.worldInfo, ambulanceteamList); + } + + this.centerIDs = new ArrayList<>(); + for (int i = 0; i < this.centerList.size(); i++) { + this.centerIDs.add(i, this.centerList.get(i).getID()); + } + for (int index = 0; index < this.clusterSize; index++) { + List entities = this.clusterEntitiesList.get(index); + List list = new ArrayList<>(entities.size()); + for (int i = 0; i < entities.size(); i++) { + list.add(i, entities.get(i).getID()); + } + this.clusterEntityIDsList.add(index, list); + } + } + + + private void calcPathBased(int repeat) { + this.initShortestPath(this.worldInfo); + Random random = new Random(); + List entityList = new ArrayList<>(this.entities); + this.centerList = new ArrayList<>(this.clusterSize); + this.clusterEntitiesList = new HashMap<>(this.clusterSize); + + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + this.centerList.add(index, entityList.get(0)); + } + for (int index = 0; index < this.clusterSize; index++) { + StandardEntity centerEntity; + do { + centerEntity = entityList + .get(Math.abs(random.nextInt()) % entityList.size()); + } while (this.centerList.contains(centerEntity)); + this.centerList.set(index, centerEntity); + } + for (int i = 0; i < repeat; i++) { + this.clusterEntitiesList.clear(); + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + } + for (StandardEntity entity : entityList) { + StandardEntity tmp = this.getNearEntity(this.worldInfo, this.centerList, + entity); + this.clusterEntitiesList.get(this.centerList.indexOf(tmp)).add(entity); + } + for (int index = 0; index < this.clusterSize; index++) { + int sumX = 0, sumY = 0; + for (StandardEntity entity : this.clusterEntitiesList.get(index)) { + Pair location = this.worldInfo.getLocation(entity); + sumX += location.first(); + sumY += location.second(); + } + int centerX = sumX / clusterEntitiesList.get(index).size(); + int centerY = sumY / clusterEntitiesList.get(index).size(); + + // this.centerList.set(index, getNearEntity(this.worldInfo, + // this.clusterEntitiesList.get(index), centerX, centerY)); + StandardEntity center = this.getNearEntity(this.worldInfo, + this.clusterEntitiesList.get(index), centerX, centerY); + if (center instanceof Area) { + this.centerList.set(index, center); + } else if (center instanceof Human) { + this.centerList.set(index, + this.worldInfo.getEntity(((Human) center).getPosition())); + } else if (center instanceof Blockade) { + this.centerList.set(index, + this.worldInfo.getEntity(((Blockade) center).getPosition())); + } + } + if (scenarioInfo.isDebugMode()) { + System.out.print("*"); + } + } + + if (scenarioInfo.isDebugMode()) { + System.out.println(); + } + + this.clusterEntitiesList.clear(); + for (int index = 0; index < this.clusterSize; index++) { + this.clusterEntitiesList.put(index, new ArrayList<>()); + } + for (StandardEntity entity : entityList) { + StandardEntity tmp = this.getNearEntity(this.worldInfo, this.centerList, + entity); + this.clusterEntitiesList.get(this.centerList.indexOf(tmp)).add(entity); + } + // this.clusterEntitiesList.sort(comparing(List::size, reverseOrder())); + if (this.assignAgentsFlag) { + List fireBrigadeList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)); + List policeForceList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.POLICE_FORCE)); + List ambulanceTeamList = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.AMBULANCE_TEAM)); + this.assignAgents(this.worldInfo, fireBrigadeList); + this.assignAgents(this.worldInfo, policeForceList); + this.assignAgents(this.worldInfo, ambulanceTeamList); + } + + this.centerIDs = new ArrayList<>(); + for (int i = 0; i < this.centerList.size(); i++) { + this.centerIDs.add(i, this.centerList.get(i).getID()); + } + for (int index = 0; index < this.clusterSize; index++) { + List entities = this.clusterEntitiesList.get(index); + List list = new ArrayList<>(entities.size()); + for (int i = 0; i < entities.size(); i++) { + list.add(i, entities.get(i).getID()); + } + this.clusterEntityIDsList.add(index, list); + } + } + + + private void assignAgents(WorldInfo world, List agentList) { + int clusterIndex = 0; + while (agentList.size() > 0) { + StandardEntity center = this.centerList.get(clusterIndex); + StandardEntity agent = this.getNearAgent(world, agentList, center); + this.clusterEntitiesList.get(clusterIndex).add(agent); + agentList.remove(agent); + clusterIndex++; + if (clusterIndex >= this.clusterSize) { + clusterIndex = 0; + } + } + } + + + private StandardEntity getNearEntityByLine(WorldInfo world, + List srcEntityList, StandardEntity targetEntity) { + Pair location = world.getLocation(targetEntity); + return this.getNearEntityByLine(world, srcEntityList, location.first(), + location.second()); + } + + + private StandardEntity getNearEntityByLine(WorldInfo world, + List srcEntityList, int targetX, int targetY) { + StandardEntity result = null; + for (StandardEntity entity : srcEntityList) { + result = ((result != null) + ? this.compareLineDistance(world, targetX, targetY, result, entity) + : entity); + } + return result; + } + + + private StandardEntity getNearAgent(WorldInfo worldInfo, + List srcAgentList, StandardEntity targetEntity) { + StandardEntity result = null; + for (StandardEntity agent : srcAgentList) { + Human human = (Human) agent; + if (result == null) { + result = agent; + } else { + if (this + .comparePathDistance(worldInfo, targetEntity, result, + worldInfo.getPosition(human)) + .equals(worldInfo.getPosition(human))) { + result = agent; + } + } + } + return result; + } + + + private StandardEntity getNearEntity(WorldInfo worldInfo, + List srcEntityList, int targetX, int targetY) { + StandardEntity result = null; + for (StandardEntity entity : srcEntityList) { + result = (result != null) + ? this.compareLineDistance(worldInfo, targetX, targetY, result, + entity) + : entity; + } + return result; + } + + + private Point2D getEdgePoint(Edge edge) { + Point2D start = edge.getStart(); + Point2D end = edge.getEnd(); + return new Point2D(((start.getX() + end.getX()) / 2.0D), + ((start.getY() + end.getY()) / 2.0D)); + } + + + private double getDistance(double fromX, double fromY, double toX, + double toY) { + double dx = fromX - toX; + double dy = fromY - toY; + return Math.hypot(dx, dy); + } + + + private double getDistance(Pair from, Point2D to) { + return getDistance(from.first(), from.second(), to.getX(), to.getY()); + } + + + private double getDistance(Pair from, Edge to) { + return getDistance(from, getEdgePoint(to)); + } + + + private double getDistance(Point2D from, Point2D to) { + return getDistance(from.getX(), from.getY(), to.getX(), to.getY()); + } + + + private double getDistance(Edge from, Edge to) { + return getDistance(getEdgePoint(from), getEdgePoint(to)); + } + + + private StandardEntity compareLineDistance(WorldInfo worldInfo, int targetX, + int targetY, StandardEntity first, StandardEntity second) { + Pair firstLocation = worldInfo.getLocation(first); + Pair secondLocation = worldInfo.getLocation(second); + double firstDistance = getDistance(firstLocation.first(), + firstLocation.second(), targetX, targetY); + double secondDistance = getDistance(secondLocation.first(), + secondLocation.second(), targetX, targetY); + return (firstDistance < secondDistance ? first : second); + } + + + private StandardEntity getNearEntity(WorldInfo worldInfo, + List srcEntityList, StandardEntity targetEntity) { + StandardEntity result = null; + for (StandardEntity entity : srcEntityList) { + result = (result != null) + ? this.comparePathDistance(worldInfo, targetEntity, result, entity) + : entity; + } + return result; + } + + + private StandardEntity comparePathDistance(WorldInfo worldInfo, + StandardEntity target, StandardEntity first, StandardEntity second) { + double firstDistance = getPathDistance(worldInfo, + shortestPath(target.getID(), first.getID())); + double secondDistance = getPathDistance(worldInfo, + shortestPath(target.getID(), second.getID())); + return (firstDistance < secondDistance ? first : second); + } + + + private double getPathDistance(WorldInfo worldInfo, List path) { + if (path == null) + return Double.MAX_VALUE; + if (path.size() <= 1) + return 0.0D; + + double distance = 0.0D; + int limit = path.size() - 1; + + Area area = (Area) worldInfo.getEntity(path.get(0)); + distance += getDistance(worldInfo.getLocation(area), + area.getEdgeTo(path.get(1))); + area = (Area) worldInfo.getEntity(path.get(limit)); + distance += getDistance(worldInfo.getLocation(area), + area.getEdgeTo(path.get(limit - 1))); + + for (int i = 1; i < limit; i++) { + area = (Area) worldInfo.getEntity(path.get(i)); + distance += getDistance(area.getEdgeTo(path.get(i - 1)), + area.getEdgeTo(path.get(i + 1))); + } + return distance; + } + + + private void initShortestPath(WorldInfo worldInfo) { + Map> neighbours = new LazyMap>() { + + @Override + public Set createValue() { + return new HashSet<>(); + } + }; + for (Entity next : worldInfo) { + if (next instanceof Area) { + Collection areaNeighbours = ((Area) next).getNeighbours(); + neighbours.get(next.getID()).addAll(areaNeighbours); + } + } + for (Map.Entry> graph : neighbours.entrySet()) {// fix + // graph + for (EntityID entityID : graph.getValue()) { + neighbours.get(entityID).add(graph.getKey()); + } + } + this.shortestPathGraph = neighbours; + } + + + private List shortestPath(EntityID start, EntityID... goals) { + return shortestPath(start, Arrays.asList(goals)); + } + + + private List shortestPath(EntityID start, + Collection goals) { + List open = new LinkedList<>(); + Map ancestors = new HashMap<>(); + open.add(start); + EntityID next; + boolean found = false; + ancestors.put(start, start); + do { + next = open.remove(0); + if (isGoal(next, goals)) { + found = true; + break; + } + Collection neighbours = shortestPathGraph.get(next); + if (neighbours.isEmpty()) + continue; + + for (EntityID neighbour : neighbours) { + if (isGoal(neighbour, goals)) { + ancestors.put(neighbour, next); + next = neighbour; + found = true; + break; + } else if (!ancestors.containsKey(neighbour)) { + open.add(neighbour); + ancestors.put(neighbour, next); + } + } + } while (!found && !open.isEmpty()); + if (!found) { + // No path + return null; + } + // Walk back from goal to start + EntityID current = next; + List path = new LinkedList<>(); + do { + path.add(0, current); + current = ancestors.get(current); + if (current == null) + throw new RuntimeException( + "Found a node with no ancestor! Something is broken."); + } while (current != start); + return path; + } + + + private boolean isGoal(EntityID e, Collection test) { + return test.contains(e); + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultChannelSubscriber.java b/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultChannelSubscriber.java new file mode 100644 index 00000000..b84ff494 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultChannelSubscriber.java @@ -0,0 +1,96 @@ +package adf_core_python.impl.module.comm; + +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.component.communication.ChannelSubscriber; +import rescuecore2.standard.entities.StandardEntityURN; + +public class DefaultChannelSubscriber extends ChannelSubscriber { + + public static int getChannelNumber(StandardEntityURN agentType, + int channelIndex, int numChannels) { + int agentIndex = 0; + if (agentType == StandardEntityURN.FIRE_BRIGADE + || agentType == StandardEntityURN.FIRE_STATION) { + agentIndex = 1; + } else if (agentType == StandardEntityURN.POLICE_FORCE + || agentType == StandardEntityURN.POLICE_OFFICE) { + agentIndex = 2; + } else if (agentType == StandardEntityURN.AMBULANCE_TEAM + || agentType == StandardEntityURN.AMBULANCE_CENTRE) { + agentIndex = 3; + } + + int index = (3 * channelIndex) + agentIndex; + if ((index % numChannels) == 0) { + index = numChannels; + } else { + index = index % numChannels; + } + return index; + } + + public static void main(String[] args) { + int numChannels = 6; + int maxChannels = 2; + for (int i = 0; i < maxChannels; i++) { + System.out.println("FIREBRIGADE-" + i + ":" + + getChannelNumber(StandardEntityURN.FIRE_BRIGADE, i, numChannels)); + } + for (int i = 0; i < maxChannels; i++) { + System.out.println("POLICE-" + i + ":" + + getChannelNumber(StandardEntityURN.POLICE_OFFICE, i, numChannels)); + } + for (int i = 0; i < maxChannels; i++) { + System.out.println("AMB-" + i + ":" + getChannelNumber( + StandardEntityURN.AMBULANCE_CENTRE, i, numChannels)); + } + } + + @Override + public void subscribe(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo, MessageManager messageManager) { + // subscribe only once at the beginning + if (agentInfo.getTime() == 1) { + int numChannels = scenarioInfo.getCommsChannelsCount() - 1; // 0th channel + // is the + // voice + // channel + + int maxChannelCount = 0; + boolean isPlatoon = isPlatoonAgent(agentInfo, worldInfo); + if (isPlatoon) { + maxChannelCount = scenarioInfo.getCommsChannelsMaxPlatoon(); + } else { + maxChannelCount = scenarioInfo.getCommsChannelsMaxOffice(); + } + + StandardEntityURN agentType = getAgentType(agentInfo, worldInfo); + int[] channels = new int[maxChannelCount]; + for (int i = 0; i < maxChannelCount; i++) { + channels[i] = getChannelNumber(agentType, i, numChannels); + } + + messageManager.subscribeToChannels(channels); + } + } + + protected boolean isPlatoonAgent(AgentInfo agentInfo, WorldInfo worldInfo) { + StandardEntityURN agentType = getAgentType(agentInfo, worldInfo); + if (agentType == StandardEntityURN.FIRE_BRIGADE + || agentType == StandardEntityURN.POLICE_FORCE + || agentType == StandardEntityURN.AMBULANCE_TEAM) { + return true; + } + return false; + } + + protected StandardEntityURN getAgentType(AgentInfo agentInfo, + WorldInfo worldInfo) { + StandardEntityURN agentType = worldInfo.getEntity(agentInfo.getID()) + .getStandardURN(); + return agentType; + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultMessageCoordinator.java b/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultMessageCoordinator.java new file mode 100644 index 00000000..07ee3f23 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/comm/DefaultMessageCoordinator.java @@ -0,0 +1,199 @@ +package adf_core_python.impl.module.comm; + +import adf.core.agent.communication.standard.bundle.StandardMessage; +import adf.core.agent.communication.standard.bundle.StandardMessagePriority; +import adf.core.agent.communication.standard.bundle.centralized.*; +import adf.core.agent.communication.standard.bundle.information.*; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.component.communication.MessageCoordinator; +import rescuecore2.standard.entities.StandardEntityURN; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultMessageCoordinator extends MessageCoordinator { + + @Override + public void coordinate(AgentInfo agentInfo, WorldInfo worldInfo, + ScenarioInfo scenarioInfo, MessageManager messageManager, + ArrayList sendMessageList, + List> channelSendMessageList) { + + // have different lists for every agent + ArrayList policeMessages = new ArrayList<>(); + ArrayList ambulanceMessages = new ArrayList<>(); + ArrayList fireBrigadeMessages = new ArrayList<>(); + + ArrayList voiceMessages = new ArrayList<>(); + + StandardEntityURN agentType = getAgentType(agentInfo, worldInfo); + + for (CommunicationMessage msg : sendMessageList) { + if (msg instanceof StandardMessage + && !((StandardMessage) msg).isRadio()) { + voiceMessages.add(msg); + } else { + if (msg instanceof MessageBuilding) { + fireBrigadeMessages.add(msg); + } else if (msg instanceof MessageCivilian) { + ambulanceMessages.add(msg); + } else if (msg instanceof MessageRoad) { + fireBrigadeMessages.add(msg); + ambulanceMessages.add(msg); + policeMessages.add(msg); + } else if (msg instanceof CommandAmbulance) { + ambulanceMessages.add(msg); + } else if (msg instanceof CommandFire) { + fireBrigadeMessages.add(msg); + } else if (msg instanceof CommandPolice) { + policeMessages.add(msg); + } else if (msg instanceof CommandScout) { + if (agentType == StandardEntityURN.FIRE_STATION) { + fireBrigadeMessages.add(msg); + } else if (agentType == StandardEntityURN.POLICE_OFFICE) { + policeMessages.add(msg); + } else if (agentType == StandardEntityURN.AMBULANCE_CENTRE) { + ambulanceMessages.add(msg); + } + } else if (msg instanceof MessageReport) { + if (agentType == StandardEntityURN.FIRE_BRIGADE) { + fireBrigadeMessages.add(msg); + } else if (agentType == StandardEntityURN.POLICE_FORCE) { + policeMessages.add(msg); + } else if (agentType == StandardEntityURN.AMBULANCE_TEAM) { + ambulanceMessages.add(msg); + } + } else if (msg instanceof MessageFireBrigade) { + fireBrigadeMessages.add(msg); + ambulanceMessages.add(msg); + policeMessages.add(msg); + } else if (msg instanceof MessagePoliceForce) { + ambulanceMessages.add(msg); + policeMessages.add(msg); + } else if (msg instanceof MessageAmbulanceTeam) { + ambulanceMessages.add(msg); + policeMessages.add(msg); + } + } + } + + if (scenarioInfo.getCommsChannelsCount() > 1) { + // send radio messages if there are more than one communication channel + int[] channelSize = new int[scenarioInfo.getCommsChannelsCount() - 1]; + + setSendMessages(scenarioInfo, StandardEntityURN.POLICE_FORCE, agentInfo, + worldInfo, policeMessages, channelSendMessageList, channelSize); + setSendMessages(scenarioInfo, StandardEntityURN.AMBULANCE_TEAM, agentInfo, + worldInfo, ambulanceMessages, channelSendMessageList, channelSize); + setSendMessages(scenarioInfo, StandardEntityURN.FIRE_BRIGADE, agentInfo, + worldInfo, fireBrigadeMessages, channelSendMessageList, channelSize); + } + + ArrayList voiceMessageLowList = new ArrayList<>(); + ArrayList voiceMessageNormalList = new ArrayList<>(); + ArrayList voiceMessageHighList = new ArrayList<>(); + + for (CommunicationMessage msg : voiceMessages) { + if (msg instanceof StandardMessage) { + StandardMessage m = (StandardMessage) msg; + switch (m.getSendingPriority()) { + case LOW: + voiceMessageLowList.add(m); + break; + case NORMAL: + voiceMessageNormalList.add(m); + break; + case HIGH: + voiceMessageHighList.add(m); + break; + } + } + } + + // set the voice channel messages + channelSendMessageList.get(0).addAll(voiceMessageHighList); + channelSendMessageList.get(0).addAll(voiceMessageNormalList); + channelSendMessageList.get(0).addAll(voiceMessageLowList); + } + + + protected int[] getChannelsByAgentType(StandardEntityURN agentType, + AgentInfo agentInfo, WorldInfo worldInfo, ScenarioInfo scenarioInfo, + int channelIndex) { + int numChannels = scenarioInfo.getCommsChannelsCount() - 1; // 0th channel + // is the voice + // channel + int maxChannelCount = 0; + boolean isPlatoon = isPlatoonAgent(agentInfo, worldInfo); + if (isPlatoon) { + maxChannelCount = scenarioInfo.getCommsChannelsMaxPlatoon(); + } else { + maxChannelCount = scenarioInfo.getCommsChannelsMaxOffice(); + } + int[] channels = new int[maxChannelCount]; + + for (int i = 0; i < maxChannelCount; i++) { + channels[i] = DefaultChannelSubscriber.getChannelNumber(agentType, i, + numChannels); + } + return channels; + } + + + protected boolean isPlatoonAgent(AgentInfo agentInfo, WorldInfo worldInfo) { + StandardEntityURN agentType = getAgentType(agentInfo, worldInfo); + if (agentType == StandardEntityURN.FIRE_BRIGADE + || agentType == StandardEntityURN.POLICE_FORCE + || agentType == StandardEntityURN.AMBULANCE_TEAM) { + return true; + } + return false; + } + + + protected StandardEntityURN getAgentType(AgentInfo agentInfo, + WorldInfo worldInfo) { + StandardEntityURN agentType = worldInfo.getEntity(agentInfo.getID()) + .getStandardURN(); + return agentType; + } + + + protected void setSendMessages(ScenarioInfo scenarioInfo, + StandardEntityURN agentType, AgentInfo agentInfo, WorldInfo worldInfo, + List messages, + List> channelSendMessageList, + int[] channelSize) { + int channelIndex = 0; + int[] channels = getChannelsByAgentType(agentType, agentInfo, worldInfo, + scenarioInfo, channelIndex); + int channel = channels[channelIndex]; + int channelCapacity = scenarioInfo.getCommsChannelBandwidth(channel); + // start from HIGH, NORMAL, to LOW + for (int i = StandardMessagePriority.values().length - 1; i >= 0; i--) { + for (CommunicationMessage msg : messages) { + StandardMessage smsg = (StandardMessage) msg; + if (smsg.getSendingPriority() == StandardMessagePriority.values()[i]) { + channelSize[channel - 1] += smsg.getByteArraySize(); + if (channelSize[channel - 1] > channelCapacity) { + channelSize[channel - 1] -= smsg.getByteArraySize(); + channelIndex++; + if (channelIndex < channels.length) { + channel = channels[channelIndex]; + channelCapacity = scenarioInfo.getCommsChannelBandwidth(channel); + channelSize[channel - 1] += smsg.getByteArraySize(); + } else { + // if there is no new channel for that message types, just break + break; + } + } + channelSendMessageList.get(channel).add(smsg); + } + } + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultAmbulanceTargetAllocator.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultAmbulanceTargetAllocator.java new file mode 100644 index 00000000..75d2b0e3 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultAmbulanceTargetAllocator.java @@ -0,0 +1,362 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.communication.standard.bundle.MessageUtil; +import adf.core.agent.communication.standard.bundle.centralized.CommandAmbulance; +import adf.core.agent.communication.standard.bundle.centralized.MessageReport; +import adf.core.agent.communication.standard.bundle.information.MessageAmbulanceTeam; +import adf.core.agent.communication.standard.bundle.information.MessageCivilian; +import adf.core.agent.communication.standard.bundle.information.MessageFireBrigade; +import adf.core.agent.communication.standard.bundle.information.MessagePoliceForce; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.AmbulanceTargetAllocator; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +import static rescuecore2.standard.entities.StandardEntityURN.REFUGE; + +public class DefaultAmbulanceTargetAllocator extends AmbulanceTargetAllocator { + + private Collection priorityHumans; + private Collection targetHumans; + + private Map ambulanceTeamInfoMap; + + public DefaultAmbulanceTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.priorityHumans = new HashSet<>(); + this.targetHumans = new HashSet<>(); + this.ambulanceTeamInfoMap = new HashMap<>(); + } + + + @Override + public AmbulanceTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.AMBULANCE_TEAM)) { + this.ambulanceTeamInfoMap.put(id, new AmbulanceTeamInfo(id)); + } + return this; + } + + + @Override + public AmbulanceTargetAllocator preparate() { + super.preparate(); + if (this.getCountPrecompute() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.AMBULANCE_TEAM)) { + this.ambulanceTeamInfoMap.put(id, new AmbulanceTeamInfo(id)); + } + return this; + } + + + @Override + public Map getResult() { + return this.convert(this.ambulanceTeamInfoMap); + } + + + @Override + public AmbulanceTargetAllocator calc() { + List agents = this + .getActionAgents(this.ambulanceTeamInfoMap); + Collection removes = new ArrayList<>(); + int currentTime = this.agentInfo.getTime(); + for (EntityID target : this.priorityHumans) { + if (agents.size() > 0) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null && targetEntity instanceof Human + && ((Human) targetEntity).isPositionDefined()) { + agents.sort(new DistanceSorter(this.worldInfo, targetEntity)); + StandardEntity result = agents.get(0); + agents.remove(0); + AmbulanceTeamInfo info = this.ambulanceTeamInfoMap + .get(result.getID()); + if (info != null) { + info.canNewAction = false; + info.target = target; + info.commandTime = currentTime; + this.ambulanceTeamInfoMap.put(result.getID(), info); + removes.add(target); + } + } + } + } + this.priorityHumans.removeAll(removes); + removes.clear(); + for (EntityID target : this.targetHumans) { + if (agents.size() > 0) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null && targetEntity instanceof Human + && ((Human) targetEntity).isPositionDefined()) { + agents.sort(new DistanceSorter(this.worldInfo, targetEntity)); + StandardEntity result = agents.get(0); + agents.remove(0); + AmbulanceTeamInfo info = this.ambulanceTeamInfoMap + .get(result.getID()); + if (info != null) { + info.canNewAction = false; + info.target = target; + info.commandTime = currentTime; + this.ambulanceTeamInfoMap.put(result.getID(), info); + removes.add(target); + } + } + } + } + this.targetHumans.removeAll(removes); + return this; + } + + + @Override + public AmbulanceTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + int currentTime = this.agentInfo.getTime(); + for (CommunicationMessage message : messageManager + .getReceivedMessageList()) { + Class messageClass = message.getClass(); + if (messageClass == MessageCivilian.class) { + MessageCivilian mc = (MessageCivilian) message; + MessageUtil.reflectMessage(this.worldInfo, mc); + if (mc.isBuriednessDefined() && mc.getBuriedness() > 0) { + this.targetHumans.add(mc.getAgentID()); + } else { + this.priorityHumans.remove(mc.getAgentID()); + this.targetHumans.remove(mc.getAgentID()); + } + } else if (messageClass == MessageFireBrigade.class) { + MessageFireBrigade mfb = (MessageFireBrigade) message; + MessageUtil.reflectMessage(this.worldInfo, mfb); + if (mfb.isBuriednessDefined() && mfb.getBuriedness() > 0) { + this.priorityHumans.add(mfb.getAgentID()); + } else { + this.priorityHumans.remove(mfb.getAgentID()); + this.targetHumans.remove(mfb.getAgentID()); + } + } else if (messageClass == MessagePoliceForce.class) { + MessagePoliceForce mpf = (MessagePoliceForce) message; + MessageUtil.reflectMessage(this.worldInfo, mpf); + if (mpf.isBuriednessDefined() && mpf.getBuriedness() > 0) { + this.priorityHumans.add(mpf.getAgentID()); + } else { + this.priorityHumans.remove(mpf.getAgentID()); + this.targetHumans.remove(mpf.getAgentID()); + } + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageAmbulanceTeam.class)) { + MessageAmbulanceTeam mat = (MessageAmbulanceTeam) message; + MessageUtil.reflectMessage(this.worldInfo, mat); + if (mat.isBuriednessDefined() && mat.getBuriedness() > 0) { + this.priorityHumans.add(mat.getAgentID()); + } else { + this.priorityHumans.remove(mat.getAgentID()); + this.targetHumans.remove(mat.getAgentID()); + } + AmbulanceTeamInfo info = this.ambulanceTeamInfoMap.get(mat.getAgentID()); + if (info == null) { + info = new AmbulanceTeamInfo(mat.getAgentID()); + } + if (currentTime >= info.commandTime + 2) { + this.ambulanceTeamInfoMap.put(mat.getAgentID(), this.update(info, mat)); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(CommandAmbulance.class)) { + CommandAmbulance command = (CommandAmbulance) message; + if (command.getAction() == CommandAmbulance.ACTION_RESCUE + && command.isBroadcast()) { + this.priorityHumans.add(command.getTargetID()); + this.targetHumans.add(command.getTargetID()); + } else if (command.getAction() == CommandAmbulance.ACTION_LOAD + && command.isBroadcast()) { + this.priorityHumans.add(command.getTargetID()); + this.targetHumans.add(command.getTargetID()); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageReport.class)) { + MessageReport report = (MessageReport) message; + AmbulanceTeamInfo info = this.ambulanceTeamInfoMap + .get(report.getSenderID()); + if (info != null && report.isDone()) { + info.canNewAction = true; + this.priorityHumans.remove(info.target); + this.targetHumans.remove(info.target); + info.target = null; + this.ambulanceTeamInfoMap.put(info.agentID, info); + } + } + return this; + } + + + private Map + convert(Map map) { + Map result = new HashMap<>(); + for (EntityID id : map.keySet()) { + AmbulanceTeamInfo info = map.get(id); + if (info != null && info.target != null) { + result.put(id, info.target); + } + } + return result; + } + + + private List + getActionAgents(Map map) { + List result = new ArrayList<>(); + for (StandardEntity entity : this.worldInfo + .getEntitiesOfType(StandardEntityURN.POLICE_FORCE)) { + AmbulanceTeamInfo info = map.get(entity.getID()); + if (info != null && info.canNewAction + && ((AmbulanceTeam) entity).isPositionDefined()) { + result.add(entity); + } + } + return result; + } + + + private AmbulanceTeamInfo update(AmbulanceTeamInfo info, + MessageAmbulanceTeam message) { + if (message.isBuriednessDefined() && message.getBuriedness() > 0) { + info.canNewAction = false; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + return info; + } + if (message.getAction() == MessageAmbulanceTeam.ACTION_REST) { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessageAmbulanceTeam.ACTION_MOVE) { + if (message.getTargetID() != null) { + StandardEntity entity = this.worldInfo.getEntity(message.getTargetID()); + if (entity != null) { + if (entity instanceof Area) { + if (entity.getStandardURN() == REFUGE) { + info.canNewAction = false; + return info; + } + StandardEntity targetEntity = this.worldInfo.getEntity(info.target); + if (targetEntity != null) { + if (targetEntity instanceof Human) { + targetEntity = this.worldInfo.getPosition((Human) targetEntity); + if (targetEntity == null) { + this.priorityHumans.remove(info.target); + this.targetHumans.remove(info.target); + info.canNewAction = true; + info.target = null; + return info; + } + } + if (targetEntity.getID().getValue() == entity.getID() + .getValue()) { + info.canNewAction = false; + } else { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } + } else { + info.canNewAction = true; + info.target = null; + } + return info; + } else if (entity instanceof Human) { + if (entity.getID().getValue() == info.target.getValue()) { + info.canNewAction = false; + } else { + info.canNewAction = true; + this.targetHumans.add(info.target); + this.targetHumans.add(entity.getID()); + info.target = null; + } + return info; + } + } + } + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessageAmbulanceTeam.ACTION_RESCUE) { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessageAmbulanceTeam.ACTION_LOAD) { + info.canNewAction = false; + } else if (message.getAction() == MessageAmbulanceTeam.ACTION_UNLOAD) { + info.canNewAction = true; + this.priorityHumans.remove(info.target); + this.targetHumans.remove(info.target); + info.target = null; + } + return info; + } + + private class AmbulanceTeamInfo { + + EntityID agentID; + EntityID target; + boolean canNewAction; + int commandTime; + + AmbulanceTeamInfo(EntityID id) { + agentID = id; + target = null; + canNewAction = true; + commandTime = -1; + } + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultBuildingDetector.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultBuildingDetector.java new file mode 100644 index 00000000..a266d8ac --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultBuildingDetector.java @@ -0,0 +1,209 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.complex.BuildingDetector; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +public class DefaultBuildingDetector extends BuildingDetector { + + private EntityID result; + + private Clustering clustering; + + public DefaultBuildingDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + switch (si.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.clustering = moduleManager.getModule( + "DefaultBuildingDetector.Clustering", + "adf_core_python_core_python.impl.module.algorithm.KMeansClustering"); + break; + } + registerModule(this.clustering); + } + + + @Override + public BuildingDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + + return this; + } + + + @Override + public BuildingDetector calc() { + this.result = this.calcTargetInCluster(); + if (this.result == null) { + this.result = this.calcTargetInWorld(); + } + return this; + } + + + private EntityID calcTargetInCluster() { + int clusterIndex = this.clustering.getClusterIndex(this.agentInfo.getID()); + Collection elements = this.clustering + .getClusterEntities(clusterIndex); + if (elements == null || elements.isEmpty()) { + return null; + } + StandardEntity me = this.agentInfo.me(); + List agents = new ArrayList<>( + this.worldInfo.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)); + Set fireBuildings = new HashSet<>(); + for (StandardEntity entity : elements) { + if (entity instanceof Building && ((Building) entity).isOnFire()) { + fireBuildings.add(entity); + } + } + for (StandardEntity entity : fireBuildings) { + if (agents.isEmpty()) { + break; + } else if (agents.size() == 1) { + if (agents.get(0).getID().getValue() == me.getID().getValue()) { + return entity.getID(); + } + break; + } + agents.sort(new DistanceSorter(this.worldInfo, entity)); + StandardEntity a0 = agents.get(0); + StandardEntity a1 = agents.get(1); + + if (me.getID().getValue() == a0.getID().getValue() + || me.getID().getValue() == a1.getID().getValue()) { + return entity.getID(); + } else { + agents.remove(a0); + agents.remove(a1); + } + } + return null; + } + + + private EntityID calcTargetInWorld() { + Collection entities = this.worldInfo.getEntitiesOfType( + StandardEntityURN.BUILDING, StandardEntityURN.GAS_STATION, + StandardEntityURN.AMBULANCE_CENTRE, StandardEntityURN.FIRE_STATION, + StandardEntityURN.POLICE_OFFICE); + StandardEntity me = this.agentInfo.me(); + List agents = new ArrayList<>( + worldInfo.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)); + Set fireBuildings = new HashSet<>(); + for (StandardEntity entity : entities) { + if (((Building) entity).isOnFire()) { + fireBuildings.add(entity); + } + } + for (StandardEntity entity : fireBuildings) { + if (agents.isEmpty()) { + break; + } else if (agents.size() == 1) { + if (agents.get(0).getID().getValue() == me.getID().getValue()) { + return entity.getID(); + } + break; + } + agents.sort(new DistanceSorter(this.worldInfo, entity)); + StandardEntity a0 = agents.get(0); + StandardEntity a1 = agents.get(1); + + if (me.getID().getValue() == a0.getID().getValue() + || me.getID().getValue() == a1.getID().getValue()) { + return entity.getID(); + } else { + agents.remove(a0); + agents.remove(a1); + } + } + return null; + } + + + @Override + public EntityID getTarget() { + return this.result; + } + + + @Override + public BuildingDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @Override + public BuildingDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @Override + public BuildingDetector preparate() { + super.preparate(); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @SuppressWarnings("unused") + private double getAngle(Vector2D v1, Vector2D v2) { + double flag = (v1.getX() * v2.getY()) - (v1.getY() * v2.getX()); + double angle = Math.acos(((v1.getX() * v2.getX()) + (v1.getY() * v2.getY())) + / (v1.getLength() * v2.getLength())); + if (flag > 0) { + return angle; + } + if (flag < 0) { + return -1 * angle; + } + return 0.0D; + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultFireTargetAllocator.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultFireTargetAllocator.java new file mode 100644 index 00000000..1d3224f0 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultFireTargetAllocator.java @@ -0,0 +1,345 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.communication.standard.bundle.MessageUtil; +import adf.core.agent.communication.standard.bundle.centralized.CommandFire; +import adf.core.agent.communication.standard.bundle.centralized.MessageReport; +import adf.core.agent.communication.standard.bundle.information.MessageCivilian; +import adf.core.agent.communication.standard.bundle.information.MessageFireBrigade; +import adf.core.agent.communication.standard.bundle.information.MessagePoliceForce; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.FireTargetAllocator; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +import static rescuecore2.standard.entities.StandardEntityURN.REFUGE; + +public class DefaultFireTargetAllocator extends FireTargetAllocator { + + private Collection priorityHumans; + private Collection targetHumans; + + private Map fireBrigadeInfoMap; + + public DefaultFireTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.priorityHumans = new HashSet<>(); + this.targetHumans = new HashSet<>(); + this.fireBrigadeInfoMap = new HashMap<>(); + } + + + @Override + public FireTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.FIRE_BRIGADE)) { + this.fireBrigadeInfoMap.put(id, new FireBrigadeInfo(id)); + } + return this; + } + + + @Override + public FireTargetAllocator preparate() { + super.preparate(); + if (this.getCountPrecompute() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.AMBULANCE_TEAM)) { + this.fireBrigadeInfoMap.put(id, new FireBrigadeInfo(id)); + } + return this; + } + + + @Override + public Map getResult() { + return this.convert(this.fireBrigadeInfoMap); + } + + + @Override + public FireTargetAllocator calc() { + List agents = this.getActionAgents(this.fireBrigadeInfoMap); + Collection removes = new ArrayList<>(); + int currentTime = this.agentInfo.getTime(); + for (EntityID target : this.priorityHumans) { + if (agents.size() > 0) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null && targetEntity instanceof Human + && ((Human) targetEntity).isPositionDefined()) { + agents.sort(new DistanceSorter(this.worldInfo, targetEntity)); + StandardEntity result = agents.get(0); + agents.remove(0); + FireBrigadeInfo info = this.fireBrigadeInfoMap.get(result.getID()); + if (info != null) { + info.canNewAction = false; + info.target = target; + info.commandTime = currentTime; + this.fireBrigadeInfoMap.put(result.getID(), info); + removes.add(target); + } + } + } + } + this.priorityHumans.removeAll(removes); + removes.clear(); + for (EntityID target : this.targetHumans) { + if (agents.size() > 0) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null && targetEntity instanceof Human + && ((Human) targetEntity).isPositionDefined()) { + agents.sort(new DistanceSorter(this.worldInfo, targetEntity)); + StandardEntity result = agents.get(0); + agents.remove(0); + FireBrigadeInfo info = this.fireBrigadeInfoMap.get(result.getID()); + if (info != null) { + info.canNewAction = false; + info.target = target; + info.commandTime = currentTime; + this.fireBrigadeInfoMap.put(result.getID(), info); + removes.add(target); + } + } + } + } + this.targetHumans.removeAll(removes); + return this; + } + + + @Override + public FireTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + int currentTime = this.agentInfo.getTime(); + for (CommunicationMessage message : messageManager + .getReceivedMessageList()) { + Class messageClass = message.getClass(); + if (messageClass == MessageCivilian.class) { + MessageCivilian mc = (MessageCivilian) message; + MessageUtil.reflectMessage(this.worldInfo, mc); + if (mc.isBuriednessDefined() && mc.getBuriedness() > 0) { + this.targetHumans.add(mc.getAgentID()); + } else { + this.priorityHumans.remove(mc.getAgentID()); + this.targetHumans.remove(mc.getAgentID()); + } + } else if (messageClass == MessageFireBrigade.class) { + MessageFireBrigade mfb = (MessageFireBrigade) message; + MessageUtil.reflectMessage(this.worldInfo, mfb); + if (mfb.isBuriednessDefined() && mfb.getBuriedness() > 0) { + this.priorityHumans.add(mfb.getAgentID()); + } else { + this.priorityHumans.remove(mfb.getAgentID()); + this.targetHumans.remove(mfb.getAgentID()); + } + } else if (messageClass == MessagePoliceForce.class) { + MessagePoliceForce mpf = (MessagePoliceForce) message; + MessageUtil.reflectMessage(this.worldInfo, mpf); + if (mpf.isBuriednessDefined() && mpf.getBuriedness() > 0) { + this.priorityHumans.add(mpf.getAgentID()); + } else { + this.priorityHumans.remove(mpf.getAgentID()); + this.targetHumans.remove(mpf.getAgentID()); + } + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageFireBrigade.class)) { + MessageFireBrigade mat = (MessageFireBrigade) message; + MessageUtil.reflectMessage(this.worldInfo, mat); + if (mat.isBuriednessDefined() && mat.getBuriedness() > 0) { + this.priorityHumans.add(mat.getAgentID()); + } else { + this.priorityHumans.remove(mat.getAgentID()); + this.targetHumans.remove(mat.getAgentID()); + } + FireBrigadeInfo info = this.fireBrigadeInfoMap.get(mat.getAgentID()); + if (info == null) { + info = new FireBrigadeInfo(mat.getAgentID()); + } + if (currentTime >= info.commandTime + 2) { + this.fireBrigadeInfoMap.put(mat.getAgentID(), this.update(info, mat)); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(CommandFire.class)) { + CommandFire command = (CommandFire) message; + if (command.getAction() == CommandFire.ACTION_RESCUE + && command.isBroadcast()) { + this.priorityHumans.add(command.getTargetID()); + this.targetHumans.add(command.getTargetID()); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageReport.class)) { + MessageReport report = (MessageReport) message; + FireBrigadeInfo info = this.fireBrigadeInfoMap.get(report.getSenderID()); + if (info != null && report.isDone()) { + info.canNewAction = true; + this.priorityHumans.remove(info.target); + this.targetHumans.remove(info.target); + info.target = null; + this.fireBrigadeInfoMap.put(info.agentID, info); + } + } + return this; + } + + + private Map convert(Map map) { + Map result = new HashMap<>(); + for (EntityID id : map.keySet()) { + FireBrigadeInfo info = map.get(id); + if (info != null && info.target != null) { + result.put(id, info.target); + } + } + return result; + } + + + private List + getActionAgents(Map map) { + List result = new ArrayList<>(); + for (StandardEntity entity : this.worldInfo + .getEntitiesOfType(StandardEntityURN.POLICE_FORCE)) { + FireBrigadeInfo info = map.get(entity.getID()); + if (info != null && info.canNewAction + && ((FireBrigade) entity).isPositionDefined()) { + result.add(entity); + } + } + return result; + } + + + private FireBrigadeInfo update(FireBrigadeInfo info, + MessageFireBrigade message) { + if (message.isBuriednessDefined() && message.getBuriedness() > 0) { + info.canNewAction = false; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + return info; + } + if (message.getAction() == MessageFireBrigade.ACTION_REST) { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessageFireBrigade.ACTION_MOVE) { + if (message.getTargetID() != null) { + StandardEntity entity = this.worldInfo.getEntity(message.getTargetID()); + if (entity != null) { + if (entity instanceof Area) { + if (entity.getStandardURN() == REFUGE) { + info.canNewAction = false; + return info; + } + StandardEntity targetEntity = this.worldInfo.getEntity(info.target); + if (targetEntity != null) { + if (targetEntity instanceof Human) { + targetEntity = this.worldInfo.getPosition((Human) targetEntity); + if (targetEntity == null) { + this.priorityHumans.remove(info.target); + this.targetHumans.remove(info.target); + info.canNewAction = true; + info.target = null; + return info; + } + } + if (targetEntity.getID().getValue() == entity.getID() + .getValue()) { + info.canNewAction = false; + } else { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } + } else { + info.canNewAction = true; + info.target = null; + } + return info; + } else if (entity instanceof Human) { + if (entity.getID().getValue() == info.target.getValue()) { + info.canNewAction = false; + } else { + info.canNewAction = true; + this.targetHumans.add(info.target); + this.targetHumans.add(entity.getID()); + info.target = null; + } + return info; + } + } + } + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessageFireBrigade.ACTION_RESCUE) { + info.canNewAction = true; + if (info.target != null) { + this.targetHumans.add(info.target); + info.target = null; + } + } + return info; + } + + private class FireBrigadeInfo { + + EntityID agentID; + EntityID target; + boolean canNewAction; + int commandTime; + + FireBrigadeInfo(EntityID id) { + agentID = id; + target = null; + canNewAction = true; + commandTime = -1; + } + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultHumanDetector.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultHumanDetector.java new file mode 100644 index 00000000..21d02623 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultHumanDetector.java @@ -0,0 +1,252 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.complex.HumanDetector; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.worldmodel.EntityID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +import static rescuecore2.standard.entities.StandardEntityURN.*; + +public class DefaultHumanDetector extends HumanDetector { + + private Clustering clustering; + + private EntityID result; + + public DefaultHumanDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + + this.result = null; + + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.clustering = moduleManager.getModule( + "DefaultHumanDetector.Clustering", + "adf_core_python.impl.module.algorithm.KMeansClustering"); + break; + } + registerModule(this.clustering); + } + + + @Override + public HumanDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() > 1) { + return this; + } + + return this; + } + + + @Override + public HumanDetector calc() { + Human transportHuman = this.agentInfo.someoneOnBoard(); + if (transportHuman != null) { + this.result = transportHuman.getID(); + return this; + } + if (this.result != null) { + Human target = (Human) this.worldInfo.getEntity(this.result); + if (target != null) { + if (!target.isHPDefined() || target.getHP() == 0) { + this.result = null; + } else if (!target.isPositionDefined()) { + this.result = null; + } else { + StandardEntity position = this.worldInfo.getPosition(target); + if (position != null) { + StandardEntityURN positionURN = position.getStandardURN(); + if (positionURN == REFUGE || positionURN == AMBULANCE_TEAM) { + this.result = null; + } + } + } + } + } + if (this.result == null) { + if (clustering == null) { + this.result = this.calcTargetInWorld(); + return this; + } + this.result = this.calcTargetInCluster(clustering); + if (this.result == null) { + this.result = this.calcTargetInWorld(); + } + } + return this; + } + + + private EntityID calcTargetInCluster(Clustering clustering) { + int clusterIndex = clustering.getClusterIndex(this.agentInfo.getID()); + Collection< + StandardEntity> elements = clustering.getClusterEntities(clusterIndex); + if (elements == null || elements.isEmpty()) { + return null; + } + + List rescueTargets = new ArrayList<>(); + List loadTargets = new ArrayList<>(); + for (StandardEntity next : this.worldInfo.getEntitiesOfType(AMBULANCE_TEAM, + FIRE_BRIGADE, POLICE_FORCE)) { + Human h = (Human) next; + if (this.agentInfo.getID().getValue() == h.getID().getValue()) { + continue; + } + StandardEntity positionEntity = this.worldInfo.getPosition(h); + if (positionEntity != null && elements.contains(positionEntity) + || elements.contains(h)) { + if (h.isHPDefined() && h.isBuriednessDefined() && h.getHP() > 0 + && h.getBuriedness() > 0) { + rescueTargets.add(h); + } + } + } + for (StandardEntity next : this.worldInfo.getEntitiesOfType(CIVILIAN)) { + Human h = (Human) next; + StandardEntity positionEntity = this.worldInfo.getPosition(h); + if (positionEntity != null && positionEntity instanceof Area) { + if (elements.contains(positionEntity)) { + if (h.isHPDefined() && h.getHP() > 0) { + if (h.isBuriednessDefined() && h.getBuriedness() > 0) { + rescueTargets.add(h); + } else { + if (h.isDamageDefined() && h.getDamage() > 0 + && positionEntity.getStandardURN() != REFUGE) { + loadTargets.add(h); + } + } + } + } + } + } + if (rescueTargets.size() > 0) { + rescueTargets + .sort(new DistanceSorter(this.worldInfo, this.agentInfo.me())); + return rescueTargets.get(0).getID(); + } + if (loadTargets.size() > 0) { + loadTargets.sort(new DistanceSorter(this.worldInfo, this.agentInfo.me())); + return loadTargets.get(0).getID(); + } + return null; + } + + + private EntityID calcTargetInWorld() { + List rescueTargets = new ArrayList<>(); + List loadTargets = new ArrayList<>(); + for (StandardEntity next : this.worldInfo.getEntitiesOfType(AMBULANCE_TEAM, + FIRE_BRIGADE, POLICE_FORCE)) { + Human h = (Human) next; + if (this.agentInfo.getID().getValue() != h.getID().getValue()) { + StandardEntity positionEntity = this.worldInfo.getPosition(h); + if (positionEntity != null && h.isHPDefined() + && h.isBuriednessDefined()) { + if (h.getHP() > 0 && h.getBuriedness() > 0) { + rescueTargets.add(h); + } + } + } + } + for (StandardEntity next : this.worldInfo.getEntitiesOfType(CIVILIAN)) { + Human h = (Human) next; + StandardEntity positionEntity = this.worldInfo.getPosition(h); + if (positionEntity != null && positionEntity instanceof Area) { + if (h.isHPDefined() && h.getHP() > 0) { + if (h.isBuriednessDefined() && h.getBuriedness() > 0) { + rescueTargets.add(h); + } else { + if (h.isDamageDefined() && h.getDamage() > 0 + && positionEntity.getStandardURN() != REFUGE) { + loadTargets.add(h); + } + } + } + } + } + if (rescueTargets.size() > 0) { + rescueTargets + .sort(new DistanceSorter(this.worldInfo, this.agentInfo.me())); + return rescueTargets.get(0).getID(); + } + if (loadTargets.size() > 0) { + loadTargets.sort(new DistanceSorter(this.worldInfo, this.agentInfo.me())); + return loadTargets.get(0).getID(); + } + return null; + } + + + @Override + public EntityID getTarget() { + return this.result; + } + + + @Override + public HumanDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @Override + public HumanDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + return this; + } + + + @Override + public HumanDetector preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + return this; + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultPoliceTargetAllocator.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultPoliceTargetAllocator.java new file mode 100644 index 00000000..688c5104 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultPoliceTargetAllocator.java @@ -0,0 +1,323 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.communication.standard.bundle.MessageUtil; +import adf.core.agent.communication.standard.bundle.centralized.CommandPolice; +import adf.core.agent.communication.standard.bundle.centralized.MessageReport; +import adf.core.agent.communication.standard.bundle.information.MessagePoliceForce; +import adf.core.agent.communication.standard.bundle.information.MessageRoad; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.complex.PoliceTargetAllocator; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +import static rescuecore2.standard.entities.StandardEntityURN.*; + +public class DefaultPoliceTargetAllocator extends PoliceTargetAllocator { + + private Collection priorityAreas; + private Collection targetAreas; + + private Map agentInfoMap; + + public DefaultPoliceTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + this.priorityAreas = new HashSet<>(); + this.targetAreas = new HashSet<>(); + this.agentInfoMap = new HashMap<>(); + } + + + @Override + public PoliceTargetAllocator resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.POLICE_FORCE)) { + this.agentInfoMap.put(id, new PoliceForceInfo(id)); + } + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE, BUILDING, + GAS_STATION)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.targetAreas.add(id); + } + } + } + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityAreas.add(id); + } + } + } + return this; + } + + + @Override + public PoliceTargetAllocator preparate() { + super.preparate(); + if (this.getCountPrecompute() >= 2) { + return this; + } + for (EntityID id : this.worldInfo + .getEntityIDsOfType(StandardEntityURN.POLICE_FORCE)) { + this.agentInfoMap.put(id, new PoliceForceInfo(id)); + } + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE, BUILDING, + GAS_STATION)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.targetAreas.add(id); + } + } + } + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityAreas.add(id); + } + } + } + return this; + } + + + @Override + public Map getResult() { + return this.convert(this.agentInfoMap); + } + + + @Override + public PoliceTargetAllocator calc() { + List agents = this.getActionAgents(this.agentInfoMap); + Collection removes = new ArrayList<>(); + int currentTime = this.agentInfo.getTime(); + for (EntityID target : this.priorityAreas) { + if (agents.size() > 0) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null) { + agents.sort(new DistanceSorter(this.worldInfo, targetEntity)); + StandardEntity result = agents.get(0); + agents.remove(0); + PoliceForceInfo info = this.agentInfoMap.get(result.getID()); + if (info != null) { + info.canNewAction = false; + info.target = target; + info.commandTime = currentTime; + this.agentInfoMap.put(result.getID(), info); + removes.add(target); + } + } + } + } + this.priorityAreas.removeAll(removes); + List areas = new ArrayList<>(); + for (EntityID target : this.targetAreas) { + StandardEntity targetEntity = this.worldInfo.getEntity(target); + if (targetEntity != null) { + areas.add(targetEntity); + } + } + for (StandardEntity agent : agents) { + if (areas.size() > 0) { + areas.sort(new DistanceSorter(this.worldInfo, agent)); + StandardEntity result = areas.get(0); + areas.remove(0); + this.targetAreas.remove(result.getID()); + PoliceForceInfo info = this.agentInfoMap.get(agent.getID()); + if (info != null) { + info.canNewAction = false; + info.target = result.getID(); + info.commandTime = currentTime; + this.agentInfoMap.put(agent.getID(), info); + } + } + } + return this; + } + + + @Override + public PoliceTargetAllocator updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + int currentTime = this.agentInfo.getTime(); + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageRoad.class)) { + MessageRoad mpf = (MessageRoad) message; + MessageUtil.reflectMessage(this.worldInfo, mpf); + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessagePoliceForce.class)) { + MessagePoliceForce mpf = (MessagePoliceForce) message; + MessageUtil.reflectMessage(this.worldInfo, mpf); + PoliceForceInfo info = this.agentInfoMap.get(mpf.getAgentID()); + if (info == null) { + info = new PoliceForceInfo(mpf.getAgentID()); + } + if (currentTime >= info.commandTime + 2) { + this.agentInfoMap.put(mpf.getAgentID(), this.update(info, mpf)); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(CommandPolice.class)) { + CommandPolice command = (CommandPolice) message; + if (command.getAction() == CommandPolice.ACTION_CLEAR + && command.isBroadcast()) { + this.priorityAreas.add(command.getTargetID()); + this.targetAreas.add(command.getTargetID()); + } + } + for (CommunicationMessage message : messageManager + .getReceivedMessageList(MessageReport.class)) { + MessageReport report = (MessageReport) message; + PoliceForceInfo info = this.agentInfoMap.get(report.getSenderID()); + if (info != null && report.isDone()) { + info.canNewAction = true; + this.priorityAreas.remove(info.target); + this.targetAreas.remove(info.target); + info.target = null; + this.agentInfoMap.put(info.agentID, info); + } + } + return this; + } + + + private PoliceForceInfo update(PoliceForceInfo info, + MessagePoliceForce message) { + if (message.isBuriednessDefined() && message.getBuriedness() > 0) { + info.canNewAction = false; + if (info.target != null) { + this.targetAreas.add(info.target); + info.target = null; + } + return info; + } + if (message.getAction() == MessagePoliceForce.ACTION_REST) { + info.canNewAction = true; + if (info.target != null) { + this.targetAreas.add(info.target); + info.target = null; + } + } else if (message.getAction() == MessagePoliceForce.ACTION_MOVE) { + if (message.getTargetID() != null) { + StandardEntity entity = this.worldInfo.getEntity(message.getTargetID()); + if (entity != null && entity instanceof Area) { + if (info.target != null) { + StandardEntity targetEntity = this.worldInfo.getEntity(info.target); + if (targetEntity != null && targetEntity instanceof Area) { + if (message.getTargetID().getValue() == info.target.getValue()) { + info.canNewAction = false; + } else { + info.canNewAction = true; + this.targetAreas.add(info.target); + info.target = null; + } + } else { + info.canNewAction = true; + info.target = null; + } + } else { + info.canNewAction = true; + } + } else { + info.canNewAction = true; + if (info.target != null) { + this.targetAreas.add(info.target); + info.target = null; + } + } + } else { + info.canNewAction = true; + if (info.target != null) { + this.targetAreas.add(info.target); + info.target = null; + } + } + } else if (message.getAction() == MessagePoliceForce.ACTION_CLEAR) { + info.canNewAction = false; + } + return info; + } + + + private List + getActionAgents(Map infoMap) { + List result = new ArrayList<>(); + for (StandardEntity entity : this.worldInfo + .getEntitiesOfType(StandardEntityURN.POLICE_FORCE)) { + PoliceForceInfo info = infoMap.get(entity.getID()); + if (info != null && info.canNewAction + && ((PoliceForce) entity).isPositionDefined()) { + result.add(entity); + } + } + return result; + } + + + private Map + convert(Map infoMap) { + Map result = new HashMap<>(); + for (EntityID id : infoMap.keySet()) { + PoliceForceInfo info = infoMap.get(id); + if (info != null && info.target != null) { + result.put(id, info.target); + } + } + return result; + } + + private class PoliceForceInfo { + + EntityID agentID; + EntityID target; + boolean canNewAction; + int commandTime; + + PoliceForceInfo(EntityID id) { + agentID = id; + target = null; + canNewAction = true; + commandTime = -1; + } + } + + private class DistanceSorter implements Comparator { + + private StandardEntity reference; + private WorldInfo worldInfo; + + DistanceSorter(WorldInfo wi, StandardEntity reference) { + this.reference = reference; + this.worldInfo = wi; + } + + + public int compare(StandardEntity a, StandardEntity b) { + int d1 = this.worldInfo.getDistance(this.reference, a); + int d2 = this.worldInfo.getDistance(this.reference, b); + return d1 - d2; + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultRoadDetector.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultRoadDetector.java new file mode 100644 index 00000000..277342c6 --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultRoadDetector.java @@ -0,0 +1,362 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.communication.standard.bundle.MessageUtil; +import adf.core.agent.communication.standard.bundle.centralized.CommandPolice; +import adf.core.agent.communication.standard.bundle.information.MessageAmbulanceTeam; +import adf.core.agent.communication.standard.bundle.information.MessageFireBrigade; +import adf.core.agent.communication.standard.bundle.information.MessagePoliceForce; +import adf.core.agent.communication.standard.bundle.information.MessageRoad; +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf.core.component.communication.CommunicationMessage; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import adf_core_python.core.component.module.complex.RoadDetector; +import rescuecore2.standard.entities.*; +import rescuecore2.worldmodel.EntityID; + +import java.util.*; + +import static rescuecore2.standard.entities.StandardEntityURN.*; + +public class DefaultRoadDetector extends RoadDetector { + + private Set targetAreas; + private Set priorityRoads; + + private PathPlanning pathPlanning; + + private EntityID result; + + public DefaultRoadDetector(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + switch (scenarioInfo.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + this.pathPlanning = moduleManager.getModule( + "DefaultRoadDetector.PathPlanning", + "adf_core_python_core_python.impl.module.algorithm.DijkstraPathPlanning"); + break; + } + registerModule(this.pathPlanning); + this.result = null; + } + + + @Override + public RoadDetector calc() { + if (this.result == null) { + EntityID positionID = this.agentInfo.getPosition(); + if (this.targetAreas.contains(positionID)) { + this.result = positionID; + return this; + } + List removeList = new ArrayList<>(this.priorityRoads.size()); + for (EntityID id : this.priorityRoads) { + if (!this.targetAreas.contains(id)) { + removeList.add(id); + } + } + this.priorityRoads.removeAll(removeList); + if (this.priorityRoads.size() > 0) { + this.pathPlanning.setFrom(positionID); + this.pathPlanning.setDestination(this.targetAreas); + List path = this.pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + this.result = path.get(path.size() - 1); + } + return this; + } + + this.pathPlanning.setFrom(positionID); + this.pathPlanning.setDestination(this.targetAreas); + List path = this.pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + this.result = path.get(path.size() - 1); + } + } + return this; + } + + + @Override + public EntityID getTarget() { + return this.result; + } + + + @Override + public RoadDetector precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @Override + public RoadDetector resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.targetAreas = new HashSet<>(); + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE, BUILDING, + GAS_STATION)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.targetAreas.add(id); + } + } + } + this.priorityRoads = new HashSet<>(); + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityRoads.add(id); + } + } + } + return this; + } + + + @Override + public RoadDetector preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.targetAreas = new HashSet<>(); + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE, BUILDING, + GAS_STATION)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.targetAreas.add(id); + } + } + } + this.priorityRoads = new HashSet<>(); + for (StandardEntity e : this.worldInfo.getEntitiesOfType(REFUGE)) { + for (EntityID id : ((Building) e).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityRoads.add(id); + } + } + } + return this; + } + + + @Override + public RoadDetector updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + if (this.result != null) { + if (this.agentInfo.getPosition().equals(this.result)) { + StandardEntity entity = this.worldInfo.getEntity(this.result); + if (entity instanceof Building) { + this.result = null; + } else if (entity instanceof Road) { + Road road = (Road) entity; + if (!road.isBlockadesDefined() || road.getBlockades().isEmpty()) { + this.targetAreas.remove(this.result); + this.result = null; + } + } + } + } + Set changedEntities = this.worldInfo.getChanged() + .getChangedEntities(); + for (CommunicationMessage message : messageManager + .getReceivedMessageList()) { + Class messageClass = message.getClass(); + if (messageClass == MessageAmbulanceTeam.class) { + this.reflectMessage((MessageAmbulanceTeam) message); + } else if (messageClass == MessageFireBrigade.class) { + this.reflectMessage((MessageFireBrigade) message); + } else if (messageClass == MessageRoad.class) { + this.reflectMessage((MessageRoad) message, changedEntities); + } else if (messageClass == MessagePoliceForce.class) { + this.reflectMessage((MessagePoliceForce) message); + } else if (messageClass == CommandPolice.class) { + this.reflectMessage((CommandPolice) message); + } + } + for (EntityID id : this.worldInfo.getChanged().getChangedEntities()) { + StandardEntity entity = this.worldInfo.getEntity(id); + if (entity instanceof Road) { + Road road = (Road) entity; + if (!road.isBlockadesDefined() || road.getBlockades().isEmpty()) { + this.targetAreas.remove(id); + } + } + } + return this; + } + + + private void reflectMessage(MessageRoad messageRoad, + Collection changedEntities) { + if (messageRoad.isBlockadeDefined() + && !changedEntities.contains(messageRoad.getBlockadeID())) { + MessageUtil.reflectMessage(this.worldInfo, messageRoad); + } + if (messageRoad.isPassable()) { + this.targetAreas.remove(messageRoad.getRoadID()); + } + } + + + private void reflectMessage(MessageAmbulanceTeam messageAmbulanceTeam) { + if (messageAmbulanceTeam.getPosition() == null) { + return; + } + if (messageAmbulanceTeam + .getAction() == MessageAmbulanceTeam.ACTION_RESCUE) { + StandardEntity position = this.worldInfo + .getEntity(messageAmbulanceTeam.getPosition()); + if (position != null && position instanceof Building) { + this.targetAreas.removeAll(((Building) position).getNeighbours()); + } + } else if (messageAmbulanceTeam + .getAction() == MessageAmbulanceTeam.ACTION_LOAD) { + StandardEntity position = this.worldInfo + .getEntity(messageAmbulanceTeam.getPosition()); + if (position != null && position instanceof Building) { + this.targetAreas.removeAll(((Building) position).getNeighbours()); + } + } else if (messageAmbulanceTeam + .getAction() == MessageAmbulanceTeam.ACTION_MOVE) { + if (messageAmbulanceTeam.getTargetID() == null) { + return; + } + StandardEntity target = this.worldInfo + .getEntity(messageAmbulanceTeam.getTargetID()); + if (target instanceof Building) { + for (EntityID id : ((Building) target).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityRoads.add(id); + } + } + } else if (target instanceof Human) { + Human human = (Human) target; + if (human.isPositionDefined()) { + StandardEntity position = this.worldInfo.getPosition(human); + if (position instanceof Building) { + for (EntityID id : ((Building) position).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityRoads.add(id); + } + } + } + } + } + } + } + + + private void reflectMessage(MessageFireBrigade messageFireBrigade) { + if (messageFireBrigade.getTargetID() == null) { + return; + } + if (messageFireBrigade.getAction() == MessageFireBrigade.ACTION_REFILL) { + StandardEntity target = this.worldInfo + .getEntity(messageFireBrigade.getTargetID()); + if (target instanceof Building) { + for (EntityID id : ((Building) target).getNeighbours()) { + StandardEntity neighbour = this.worldInfo.getEntity(id); + if (neighbour instanceof Road) { + this.priorityRoads.add(id); + } + } + } else if (target.getStandardURN() == HYDRANT) { + this.priorityRoads.add(target.getID()); + this.targetAreas.add(target.getID()); + } + } + } + + + private void reflectMessage(MessagePoliceForce messagePoliceForce) { + if (messagePoliceForce.getAction() == MessagePoliceForce.ACTION_CLEAR) { + if (messagePoliceForce.getAgentID().getValue() != this.agentInfo.getID() + .getValue()) { + if (messagePoliceForce.isTargetDefined()) { + EntityID targetID = messagePoliceForce.getTargetID(); + if (targetID == null) { + return; + } + StandardEntity entity = this.worldInfo.getEntity(targetID); + if (entity == null) { + return; + } + + if (entity instanceof Area) { + this.targetAreas.remove(targetID); + if (this.result != null + && this.result.getValue() == targetID.getValue()) { + if (this.agentInfo.getID().getValue() < messagePoliceForce + .getAgentID().getValue()) { + this.result = null; + } + } + } else if (entity.getStandardURN() == BLOCKADE) { + EntityID position = ((Blockade) entity).getPosition(); + this.targetAreas.remove(position); + if (this.result != null + && this.result.getValue() == position.getValue()) { + if (this.agentInfo.getID().getValue() < messagePoliceForce + .getAgentID().getValue()) { + this.result = null; + } + } + } + + } + } + } + } + + + private void reflectMessage(CommandPolice commandPolice) { + boolean flag = false; + if (commandPolice.isToIDDefined() && this.agentInfo.getID() + .getValue() == commandPolice.getToID().getValue()) { + flag = true; + } else if (commandPolice.isBroadcast()) { + flag = true; + } + if (flag && commandPolice.getAction() == CommandPolice.ACTION_CLEAR) { + if (commandPolice.getTargetID() == null) { + return; + } + StandardEntity target = this.worldInfo + .getEntity(commandPolice.getTargetID()); + if (target instanceof Area) { + this.priorityRoads.add(target.getID()); + this.targetAreas.add(target.getID()); + } else if (target.getStandardURN() == BLOCKADE) { + Blockade blockade = (Blockade) target; + if (blockade.isPositionDefined()) { + this.priorityRoads.add(blockade.getPosition()); + this.targetAreas.add(blockade.getPosition()); + } + } + } + } +} diff --git a/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultSearch.java b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultSearch.java new file mode 100644 index 00000000..c75c7edc --- /dev/null +++ b/java/lib/src/main/java/adf_core_python/impl/module/complex/DefaultSearch.java @@ -0,0 +1,164 @@ +package adf_core_python.impl.module.complex; + +import adf.core.agent.develop.DevelopData; +import adf.core.agent.info.ScenarioInfo; +import adf.core.agent.info.WorldInfo; +import adf_core_python.core.agent.communication.MessageManager; +import adf_core_python.core.agent.info.AgentInfo; +import adf_core_python.core.agent.module.ModuleManager; +import adf_core_python.core.agent.precompute.PrecomputeData; +import adf_core_python.core.component.module.algorithm.Clustering; +import adf_core_python.core.component.module.algorithm.PathPlanning; +import adf_core_python.core.component.module.complex.Search; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.worldmodel.EntityID; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import static rescuecore2.standard.entities.StandardEntityURN.*; + +public class DefaultSearch extends Search { + + private PathPlanning pathPlanning; + private Clustering clustering; + + private EntityID result; + private Collection unsearchedBuildingIDs; + + public DefaultSearch(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) { + super(ai, wi, si, moduleManager, developData); + + this.unsearchedBuildingIDs = new HashSet<>(); + + StandardEntityURN agentURN = ai.me().getStandardURN(); + switch (si.getMode()) { + case PRECOMPUTATION_PHASE: + case PRECOMPUTED: + case NON_PRECOMPUTE: + if (agentURN == AMBULANCE_TEAM) { + this.pathPlanning = moduleManager.getModule( + "DefaultSearch.PathPlanning.Ambulance", + "adf_core_python_core_python.impl.module.algorithm.DijkstraPathPlanning"); + this.clustering = moduleManager.getModule( + "DefaultSearch.Clustering.Ambulance", + "adf_core_python.impl.module.algorithm.KMeansClustering"); + } else if (agentURN == FIRE_BRIGADE) { + this.pathPlanning = moduleManager.getModule( + "DefaultSearch.PathPlanning.Fire", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + this.clustering = moduleManager.getModule( + "DefaultSearch.Clustering.Fire", + "adf_core_python.impl.module.algorithm.KMeansClustering"); + } else if (agentURN == POLICE_FORCE) { + this.pathPlanning = moduleManager.getModule( + "DefaultSearch.PathPlanning.Police", + "adf_core_python.impl.module.algorithm.DijkstraPathPlanning"); + this.clustering = moduleManager.getModule( + "DefaultSearch.Clustering.Police", + "adf_core_python.impl.module.algorithm.KMeansClustering"); + } + break; + } + + registerModule(this.pathPlanning); + registerModule(this.clustering); + } + + + @Override + public Search updateInfo(MessageManager messageManager) { + super.updateInfo(messageManager); + if (this.getCountUpdateInfo() >= 2) { + return this; + } + + this.unsearchedBuildingIDs + .removeAll(this.worldInfo.getChanged().getChangedEntities()); + + if (this.unsearchedBuildingIDs.isEmpty()) { + this.reset(); + this.unsearchedBuildingIDs + .removeAll(this.worldInfo.getChanged().getChangedEntities()); + } + return this; + } + + + @Override + public Search calc() { + this.result = null; + this.pathPlanning.setFrom(this.agentInfo.getPosition()); + this.pathPlanning.setDestination(this.unsearchedBuildingIDs); + List path = this.pathPlanning.calc().getResult(); + if (path != null && path.size() > 0) { + this.result = path.get(path.size() - 1); + } + return this; + } + + + private void reset() { + this.unsearchedBuildingIDs.clear(); + + Collection clusterEntities = null; + if (this.clustering != null) { + int clusterIndex = this.clustering + .getClusterIndex(this.agentInfo.getID()); + clusterEntities = this.clustering.getClusterEntities(clusterIndex); + + } + if (clusterEntities != null && clusterEntities.size() > 0) { + for (StandardEntity entity : clusterEntities) { + if (entity instanceof Building && entity.getStandardURN() != REFUGE) { + this.unsearchedBuildingIDs.add(entity.getID()); + } + } + } else { + this.unsearchedBuildingIDs + .addAll(this.worldInfo.getEntityIDsOfType(BUILDING, GAS_STATION, + AMBULANCE_CENTRE, FIRE_STATION, POLICE_OFFICE)); + } + } + + + @Override + public EntityID getTarget() { + return this.result; + } + + + @Override + public Search precompute(PrecomputeData precomputeData) { + super.precompute(precomputeData); + if (this.getCountPrecompute() >= 2) { + return this; + } + return this; + } + + + @Override + public Search resume(PrecomputeData precomputeData) { + super.resume(precomputeData); + if (this.getCountResume() >= 2) { + return this; + } + this.worldInfo.requestRollback(); + return this; + } + + + @Override + public Search preparate() { + super.preparate(); + if (this.getCountPreparate() >= 2) { + return this; + } + this.worldInfo.requestRollback(); + return this; + } +} diff --git a/java/lib/src/main/resources/log4j2.xml b/java/lib/src/main/resources/log4j2.xml new file mode 100644 index 00000000..893c33d7 --- /dev/null +++ b/java/lib/src/main/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/java/lib/src/test/java/org/example/LibraryTest.java b/java/lib/src/test/java/org/example/LibraryTest.java new file mode 100644 index 00000000..01e3af05 --- /dev/null +++ b/java/lib/src/test/java/org/example/LibraryTest.java @@ -0,0 +1,14 @@ +/* + * This source file was generated by the Gradle 'init' task + */ +package org.example; + +import org.junit.jupiter.api.Test; + +class LibraryTest { + @Test + void someLibraryMethodReturnsTrue() { +// Library classUnderTest = new Library(); +// assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'"); + } +} diff --git a/java/settings.gradle b/java/settings.gradle new file mode 100644 index 00000000..2e78246b --- /dev/null +++ b/java/settings.gradle @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.10.2/userguide/multi_project_builds.html in the Gradle documentation. + */ + +plugins { + // Apply the foojay-resolver plugin to allow automatic download of JDKs + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} + +rootProject.name = 'adf-core-python' +include('lib') diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..7b797ccc --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,5 @@ +jdk: + - openjdk17 + +before_install: + - cd java \ No newline at end of file diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index c0cd004f..00000000 --- a/poetry.lock +++ /dev/null @@ -1,258 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "mslex" -version = "1.2.0" -description = "shlex for windows" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mslex-1.2.0-py3-none-any.whl", hash = "sha256:c68ec637485ee3544c5847c1b4e78b02940b32708568fb1d8715491815aa2341"}, - {file = "mslex-1.2.0.tar.gz", hash = "sha256:79e2abc5a129dd71cdde58a22a2039abb7fa8afcbac498b723ba6e9b9fbacc14"}, -] - -[[package]] -name = "mypy" -version = "1.11.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -typing-extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, - {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, - {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, - {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, - {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, - {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, - {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, - {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, - {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, - {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, - {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, - {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, - {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, - {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, - {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, - {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, -] - -[[package]] -name = "psutil" -version = "5.9.8" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "rcrs_core" -version = "0.1.0" -description = "RoboCup Rescue Simulation(RCRS Agent Development Library)" -optional = false -python-versions = "*" -files = [] -develop = false - -[package.dependencies] -protobuf = "==3.20.*" -rtree = "*" - -[package.source] -type = "git" -url = "https://github.com/adf-python/rcrs-core-python" -reference = "HEAD" -resolved_reference = "db980d7e3487be063545fd95b3495fd4acbc0b11" - -[[package]] -name = "rtree" -version = "1.3.0" -description = "R-Tree spatial index for Python GIS" -optional = false -python-versions = ">=3.8" -files = [ - {file = "Rtree-1.3.0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:80879d9db282a2273ca3a0d896c84583940e9777477727a277624ebfd424c517"}, - {file = "Rtree-1.3.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4328e9e421797c347e6eb08efbbade962fe3664ebd60c1dffe82c40911b1e125"}, - {file = "Rtree-1.3.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:037130d3ce1fc029de81941ec416ba5546f66228380ba19bb41f2ea1294e8423"}, - {file = "Rtree-1.3.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:864a05d0c3b7ce6c5e34378b7ab630057603b79179368bc50624258bdf2ff631"}, - {file = "Rtree-1.3.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ec2ed6d1635753dab966e68f592a9c4896f3f4ec6ad2b09b776d592eacd883a9"}, - {file = "Rtree-1.3.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b4485fb3e5c5e85b94a95f0a930a3848e040d2699cfb012940ba5b0130f1e09a"}, - {file = "Rtree-1.3.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7e2e9211f4fb404c06a08fd2cbebb03234214f73c51913bb371c3d9954e99cc9"}, - {file = "Rtree-1.3.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c021f4772b25cc24915da8073e553ded6fa8d0b317caa4202255ed26b2344c1c"}, - {file = "Rtree-1.3.0-py3-none-win_amd64.whl", hash = "sha256:97f835801d24c10bbf02381abe5e327345c8296ec711dde7658792376abafc66"}, - {file = "rtree-1.3.0.tar.gz", hash = "sha256:b36e9dd2dc60ffe3d02e367242d2c26f7281b00e1aaf0c39590442edaaadd916"}, -] - -[[package]] -name = "ruff" -version = "0.4.10" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, - {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, - {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, - {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, - {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, - {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, -] - -[[package]] -name = "taskipy" -version = "1.13.0" -description = "tasks runner for python projects" -optional = false -python-versions = "<4.0,>=3.6" -files = [ - {file = "taskipy-1.13.0-py3-none-any.whl", hash = "sha256:56f42b7e508d9aed2c7b6365f8d3dab62dbd0c768c1ab606c819da4fc38421f7"}, - {file = "taskipy-1.13.0.tar.gz", hash = "sha256:2b52f0257958fed151f1340f7de93fcf0848f7a358ad62ba05c31c2ca04f89fe"}, -] - -[package.dependencies] -colorama = ">=0.4.4,<0.5.0" -mslex = {version = ">=1.1.0,<2.0.0", markers = "sys_platform == \"win32\""} -psutil = ">=5.7.2,<6.0.0" -tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "types-protobuf" -version = "4.25.0.20240417" -description = "Typing stubs for protobuf" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-protobuf-4.25.0.20240417.tar.gz", hash = "sha256:c34eff17b9b3a0adb6830622f0f302484e4c089f533a46e3f147568313544352"}, - {file = "types_protobuf-4.25.0.20240417-py3-none-any.whl", hash = "sha256:e9b613227c2127e3d4881d75d93c93b4d6fd97b5f6a099a0b654a05351c8685d"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.12" -content-hash = "694c9005401e153d18e3d6699b5f77a2789d41cbbbc8d06445640ba26b4f5e7e" diff --git a/poetry.toml b/poetry.toml deleted file mode 100644 index ab1033bd..00000000 --- a/poetry.toml +++ /dev/null @@ -1,2 +0,0 @@ -[virtualenvs] -in-project = true diff --git a/pyproject.toml b/pyproject.toml index 87ca343e..b53e1f38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,34 +1,47 @@ -[tool.poetry] +[project] name = "adf_core_python" -version = "0.1.0" +version = "0.2.1" description = "Agent Development Framework for Python" -authors = [ - "Haruki Uehara ", - "Yuki Shimada ", - ] readme = "README.md" -package-mode = true - -[tool.poetry.dependencies] -python = "^3.12" -rcrs_core = {git = "https://github.com/adf-python/rcrs-core-python"} - - -[tool.poetry.group.dev.dependencies] -taskipy = "^1.12.2" -mypy = "^1.9.0" -types-protobuf = "^4.25.0.20240410" -ruff = "^0.4.4" +authors = [ + { name = "Haruki Uehara", email = "k19016kk@maslab.aitech.ac.jp"}, + { name = "Yuki Shimada", email = "shimapaca@maslab.aitech.ac.jp" } +] +requires-python = ">=3.13" +dependencies = [ + "bitarray>=3.6.0", + "click>=8.2.1", + "jinja2>=3.1.6", + "protobuf>=6.31.1", + "pyyaml>=6.0.2", + "rcrscore", + "rtree>=1.4.0", + "scikit-learn>=1.7.1", + "shapely>=2.1.1", + "structlog>=25.4.0", + "types-pyyaml>=6.0.12.20250516", +] [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["uv_build>=0.8.2,<0.9.0"] +build-backend = "uv_build" + +[dependency-groups] +dev = [ + "mypy>=1.17.1", + "myst-parser>=4.0.1", + "pytest>=8.4.1", + "ruff>=0.12.5", + "sphinx>=8.2.3", + "sphinx-book-theme>=1.1.4", + "sphinx-copybutton>=0.5.2", + "sphinx-intl>=2.3.2", + "sphinx-rtd-theme>=3.0.2", + "sphinx-togglebutton>=0.3.2", + "sphinxcontrib-mermaid>=1.0.0", + "types-protobuf>=6.30.2.20250703", +] -[tool.taskipy.tasks] -format = "ruff format ." -lint = "ruff check ." -typecheck = "mypy ." -precommit = "task format && task lint && task typecheck" [tool.ruff] # Exclude a variety of commonly ignored directories. @@ -60,11 +73,13 @@ exclude = [ "site-packages", "venv", "pb2", + "docs", + "proto", ] # Same as Black. line-length = 88 -indent-width = 4 +indent-width = 2 # Assume Python 3.12 target-version = "py312" @@ -73,7 +88,7 @@ target-version = "py312" # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. # Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or # McCabe complexity (`C901`) by default. -select = ["E4", "E7", "E9", "F"] +select = ["E4", "E7", "E9", "F", "N8"] ignore = [] # Allow fix for all enabled rules (when `--fix`) is provided. @@ -112,9 +127,17 @@ docstring-code-line-length = "dynamic" [tool.mypy] ignore_missing_imports = true -show_error_context = true # エラー時のメッセージを詳細表示 -show_column_numbers = true # エラー発生箇所の行数/列数を表示 -disallow_untyped_defs = true # 関数定義の引数/戻り値に型アノテーション必須 -no_implicit_optional = true # デフォルト引数に None を取る場合型アノテーションに Optional 必須 -check_untyped_defs = true # 型注釈がない関数やメソッドに対して型チェックを行う -warn_redundant_casts = true # 冗長なキャストに警告 +show_error_context = true +show_column_numbers = true +disallow_untyped_defs = true +no_implicit_optional = true +check_untyped_defs = true +warn_redundant_casts = true +exclude = ["docs"] + +[tool.pytest.ini_options] +pythonpath = ["src"] +testpaths = ["tests"] + +[tool.uv.sources] +rcrscore = { git = "https://github.com/adf-python/rcrs-core-python" , tag = "v0.2.0" } diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 00000000..972999c8 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,4 @@ +{ + "typeCheckingMode": "standard", + "include": ["adf_core_python"] +} diff --git a/src/adf_core_python/__init__.py b/src/adf_core_python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/cli/__init__.py b/src/adf_core_python/cli/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/cli/cli.py b/src/adf_core_python/cli/cli.py new file mode 100644 index 00000000..abaf673f --- /dev/null +++ b/src/adf_core_python/cli/cli.py @@ -0,0 +1,56 @@ +import os +import shutil + +import click + +NAME_PLACEHOLDER = "team_name" + + +@click.command() +@click.option( + "--name", prompt="Your agent team name", help="The name of your agent team" +) +def cli(name: str) -> None: + # load template dir and create a new agent team + click.echo(f"Creating a new agent team with name: {name}") + # 自身がいるディレクトリを取得 + template_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "template") + # コマンドラインのカレントディレクトリを取得 + current_dir = os.getcwd() + + _copy_template( + template_dir, + current_dir, + name, + ) + + +def _copy_template( + src: str, + dest: str, + name: str, +) -> None: + dest = os.path.join(dest, name) + shutil.copytree(src, dest) + + # dest以下のファイル内のNAME_PLACEHOLDERをnameに置換 + for root, dirs, files in os.walk(dest): + for file in files: + file_path = os.path.join(root, file) + with open(file_path, "r") as f: + if not file_path.endswith((".py", ".yaml", ".json")): + continue + content = f.read() + with open(file_path, "w") as f: + f.write(content.replace(NAME_PLACEHOLDER, name)) + + # ディレクトリ名のNAME_PLACEHOLDERをnameに置換 + for root, dirs, files in os.walk(dest): + for dir in dirs: + dir_path = os.path.join(root, dir) + new_dir_path = dir_path.replace(NAME_PLACEHOLDER, name) + os.rename(dir_path, new_dir_path) + + +if __name__ == "__main__": + cli() diff --git a/src/adf_core_python/cli/template/config/development.json b/src/adf_core_python/cli/template/config/development.json new file mode 100644 index 00000000..d0ae716d --- /dev/null +++ b/src/adf_core_python/cli/template/config/development.json @@ -0,0 +1,3 @@ +{ + "test": "test" +} diff --git a/src/adf_core_python/cli/template/config/launcher.yaml b/src/adf_core_python/cli/template/config/launcher.yaml new file mode 100644 index 00000000..a47a583d --- /dev/null +++ b/src/adf_core_python/cli/template/config/launcher.yaml @@ -0,0 +1,35 @@ +kernel: + host: localhost + port: 27931 + +team: + name: team_name + +adf: + launcher: + precompute: 0 + debug: + flag: 0 + agent: + moduleconfig: + filename: config/module.yaml + + develop: + flag: 1 + filename: config/development.json + + team: + platoon: + ambulance: + count: 100 + fire: + count: 100 + police: + count: 100 + office: + ambulance: + count: 5 + fire: + count: 5 + police: + count: 5 diff --git a/src/adf_core_python/cli/template/config/module.yaml b/src/adf_core_python/cli/template/config/module.yaml new file mode 100644 index 00000000..0079ab99 --- /dev/null +++ b/src/adf_core_python/cli/template/config/module.yaml @@ -0,0 +1,85 @@ +DefaultTacticsAmbulanceTeam: + HumanDetector: src.team_name.module.complex.sample_human_detector.SampleHumanDetector + Search: src.team_name.module.complex.sample_search.SampleSearch + ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorAmbulance: adf_core_python.implement.centralized.default_command_executor_ambulance.DefaultCommandExecutorAmbulance + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsFireBrigade: + HumanDetector: src.team_name.module.complex.sample_human_detector.SampleHumanDetector + Search: src.team_name.module.complex.sample_search.SampleSearch + ExtendActionRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorFire: adf_core_python.implement.centralized.default_command_executor_fire.DefaultCommandExecutorFire + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsPoliceForce: + RoadDetector: src.team_name.module.complex.sample_road_detector.SampleRoadDetector + Search: src.team_name.module.complex.sample_search.SampleSearch + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + CommandExecutorPolice: adf_core_python.implement.centralized.default_command_executor_police.DefaultCommandExecutorPolice + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout + +DefaultTacticsAmbulanceCenter: + TargetAllocator: adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_ambulance.DefaultCommandPickerAmbulance + +DefaultTacticsFireStation: + TargetAllocator: adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_fire.DefaultCommandPickerFire + +DefaultTacticsPoliceOffice: + TargetAllocator: adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.default_command_picker_police.DefaultCommandPickerPolice + +SampleSearch: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + Clustering: adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering + +SampleRoadDetector: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +SampleHumanDetector: + Clustering: adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering + +DefaultExtendActionClear: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionRescue: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionMove: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultExtendActionTransport: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultCommandExecutorAmbulance: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorFire: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionFireRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove + +DefaultCommandExecutorScout: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + +DefaultCommandExecutorScoutPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + +MessageManager: + PlatoonChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + CenterChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + PlatoonMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator + CenterMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator diff --git a/src/adf_core_python/cli/template/main.py b/src/adf_core_python/cli/template/main.py new file mode 100644 index 00000000..8630da13 --- /dev/null +++ b/src/adf_core_python/cli/template/main.py @@ -0,0 +1,5 @@ +from adf_core_python.launcher import Launcher + +if __name__ == "__main__": + launcher = Launcher("./config/launcher.yaml") + launcher.launch() diff --git a/src/adf_core_python/cli/template/src/team_name/__init__.py b/src/adf_core_python/cli/template/src/team_name/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/cli/template/src/team_name/module/__init__.py b/src/adf_core_python/cli/template/src/team_name/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/cli/template/src/team_name/module/complex/__init__.py b/src/adf_core_python/cli/template/src/team_name/module/complex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/cli/template/src/team_name/module/complex/sample_human_detector.py b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_human_detector.py new file mode 100644 index 00000000..b782c9da --- /dev/null +++ b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_human_detector.py @@ -0,0 +1,148 @@ +from typing import Optional, cast + +from rcrscore.entities import Civilian, Entity, EntityID, Human +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.logger.logger import get_agent_logger + + +class SampleHumanDetector(HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "SampleHumanDetector.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + self.register_sub_module(self._clustering) + + self._result: Optional[EntityID] = None + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def calculate(self) -> HumanDetector: + transport_human: Optional[Human] = self._agent_info.some_one_on_board() + if transport_human is not None: + self._result = transport_human.get_entity_id() + return self + + if self._result is not None: + if not self._is_valid_human(self._result): + self._result = None + + if self._result is None: + self._result = self._select_target() + + return self + + def _select_target(self) -> Optional[EntityID]: + if self._result is not None and self._is_valid_human(self._result): + return self._result + + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + + cluster_valid_human_entities: list[Entity] = [ + entity + for entity in cluster_entities + if self._is_valid_human(entity.get_entity_id()) and isinstance(entity, Civilian) + ] + if len(cluster_valid_human_entities) != 0: + nearest_human_entity = cluster_valid_human_entities[0] + nearest_distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + nearest_human_entity.get_entity_id(), + ) + for entity in cluster_valid_human_entities: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + entity.get_entity_id(), + ) + if distance < nearest_distance: + nearest_distance = distance + nearest_human_entity = entity + return nearest_human_entity.get_entity_id() + + world_valid_human_entities: list[Entity] = [ + entity + for entity in self._world_info.get_entities_of_types([Civilian]) + if self._is_valid_human(entity.get_entity_id()) + ] + if len(world_valid_human_entities) != 0: + nearest_human_entity = world_valid_human_entities[0] + nearest_distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + nearest_human_entity.get_entity_id(), + ) + for entity in world_valid_human_entities: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + entity.get_entity_id(), + ) + if distance < nearest_distance: + nearest_distance = distance + nearest_human_entity = entity + return nearest_human_entity.get_entity_id() + + return None + + def _is_valid_human(self, target_entity_id: EntityID) -> bool: + target: Optional[Entity] = self._world_info.get_entity(target_entity_id) + if target is None: + return False + if not isinstance(target, Human): + return False + hp: Optional[int] = target.get_hp() + if hp is None or hp <= 0: + return False + buriedness: Optional[int] = target.get_buriedness() + if buriedness is None: + return False + myself = self._agent_info.get_myself() + if myself is None: + return False + if myself.get_urn() == EntityURN.FIRE_BRIGADE and buriedness == 0: + return False + if myself.get_urn() == EntityURN.AMBULANCE_TEAM and buriedness > 0: + return False + damage: Optional[int] = target.get_damage() + if damage is None or damage == 0: + return False + position_entity_id: Optional[EntityID] = target.get_position() + if position_entity_id is None: + return False + position: Optional[Entity] = self._world_info.get_entity(position_entity_id) + if position is None: + return False + urn: EntityURN = position.get_urn() + if urn == EntityURN.REFUGE or urn == EntityURN.AMBULANCE_TEAM: + return False + + return True + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result diff --git a/src/adf_core_python/cli/template/src/team_name/module/complex/sample_road_detector.py b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_road_detector.py new file mode 100644 index 00000000..4ec12954 --- /dev/null +++ b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_road_detector.py @@ -0,0 +1,152 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, EntityID, GasStation, Refuge, Road + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.algorithm.path_planning import ( + PathPlanning, +) +from adf_core_python.core.component.module.complex.road_detector import RoadDetector + + +class SampleRoadDetector(RoadDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "SampleRoadDetector.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self.register_sub_module(self._path_planning) + self._result: Optional[EntityID] = None + + def precompute(self, precompute_data: PrecomputeData) -> RoadDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> RoadDetector: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + + self._target_areas: set[EntityID] = set() + entities = self._world_info.get_entities_of_types([Refuge, Building, GasStation]) + for entity in entities: + if not isinstance(entity, Building): + continue + for entity_id in entity.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + self._priority_roads = set() + for entity in self._world_info.get_entities_of_types([Refuge]): + if not isinstance(entity, Building): + continue + for entity_id in entity.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_roads.add(entity_id) + + return self + + def prepare(self) -> RoadDetector: + super().prepare() + if self.get_count_prepare() >= 2: + return self + + self._target_areas = set() + entities = self._world_info.get_entities_of_types([Refuge, Building, GasStation]) + for entity in entities: + building: Building = cast(Building, entity) + for entity_id in building.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + self._priority_roads = set() + for entity in self._world_info.get_entities_of_types([Refuge]): + refuge: Refuge = cast(Refuge, entity) + for entity_id in refuge.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_roads.add(entity_id) + + return self + + def update_info(self, message_manager: MessageManager) -> RoadDetector: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + if self._result is not None: + if self._agent_info.get_position_entity_id == self._result: + entity = self._world_info.get_entity(self._result) + if isinstance(entity, Building): + self._result = None + elif isinstance(entity, Road): + road = entity + if road.get_blockades() == []: + self._target_areas.remove(self._result) + self._result = None + + return self + + def calculate(self) -> RoadDetector: + if self._result is None: + position_entity_id = self._agent_info.get_position_entity_id() + if position_entity_id is None: + return self + if position_entity_id in self._target_areas: + self._result = position_entity_id + return self + remove_list = [] + for entity_id in self._priority_roads: + if entity_id not in self._target_areas: + remove_list.append(entity_id) + + self._priority_roads = self._priority_roads - set(remove_list) + if len(self._priority_roads) > 0: + agent_position = self._agent_info.get_position_entity_id() + if agent_position is None: + return self + _nearest_target_area = agent_position + _nearest_distance = float("inf") + for target_area in self._target_areas: + if ( + self._world_info.get_distance(agent_position, target_area) + < _nearest_distance + ): + _nearest_target_area = target_area + _nearest_distance = self._world_info.get_distance( + agent_position, target_area + ) + path: list[EntityID] = self._path_planning.get_path( + agent_position, _nearest_target_area + ) + if path is not None and len(path) > 0: + self._result = path[-1] + + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result diff --git a/src/adf_core_python/cli/template/src/team_name/module/complex/sample_search.py b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_search.py new file mode 100644 index 00000000..9da8af33 --- /dev/null +++ b/src/adf_core_python/cli/template/src/team_name/module/complex/sample_search.py @@ -0,0 +1,104 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, Entity, EntityID, Refuge + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.logger.logger import get_agent_logger + + +class SampleSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._unreached_building_ids: set[EntityID] = set() + self._result: Optional[EntityID] = None + + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "SampleSearch.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "SampleSearch.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self.register_sub_module(self._clustering) + self.register_sub_module(self._path_planning) + + def update_info(self, message_manager: MessageManager) -> Search: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + + self._logger.debug( + f"unreached_building_ids: {[str(id) for id in self._unreached_building_ids]}" + ) + + searched_building_id = self._agent_info.get_position_entity_id() + if searched_building_id is not None: + self._unreached_building_ids.discard(searched_building_id) + + if len(self._unreached_building_ids) == 0: + self._unreached_building_ids = self._get_search_targets() + + return self + + def calculate(self) -> Search: + nearest_building_id: Optional[EntityID] = None + nearest_distance: Optional[float] = None + for building_id in self._unreached_building_ids: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), building_id + ) + if nearest_distance is None or distance < nearest_distance: + nearest_building_id = building_id + nearest_distance = distance + self._result = nearest_building_id + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result + + def _get_search_targets(self) -> set[EntityID]: + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + building_entity_ids: list[EntityID] = [ + entity.get_entity_id() + for entity in cluster_entities + if isinstance(entity, Building) and not isinstance(entity, Refuge) + ] + + return set(building_entity_ids) diff --git a/src/adf_core_python/core/__init__.py b/src/adf_core_python/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/__init__.py b/src/adf_core_python/core/agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/__init__.py b/src/adf_core_python/core/agent/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/action.py b/src/adf_core_python/core/agent/action/action.py new file mode 100644 index 00000000..551d18eb --- /dev/null +++ b/src/adf_core_python/core/agent/action/action.py @@ -0,0 +1,13 @@ +from abc import ABC, abstractmethod + +from rcrscore.commands import Command +from rcrscore.entities import EntityID + + +class Action(ABC): + def __init__(self) -> None: + pass + + @abstractmethod + def get_command(self, agent_id: EntityID, time: int) -> Command: + raise NotImplementedError diff --git a/src/adf_core_python/core/agent/action/ambulance/__init__.py b/src/adf_core_python/core/agent/action/ambulance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/ambulance/action_load.py b/src/adf_core_python/core/agent/action/ambulance/action_load.py new file mode 100644 index 00000000..572b50be --- /dev/null +++ b/src/adf_core_python/core/agent/action/ambulance/action_load.py @@ -0,0 +1,15 @@ +from rcrscore.commands import AKLoad, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionLoad(Action): + def __init__(self, target_id: EntityID) -> None: + self.target_id = target_id + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKLoad(agent_id, time, self.target_id) + + def __str__(self) -> str: + return f"ActionLoad(target_id={self.target_id})" diff --git a/src/adf_core_python/core/agent/action/ambulance/action_rescue.py b/src/adf_core_python/core/agent/action/ambulance/action_rescue.py new file mode 100644 index 00000000..8e3ce84d --- /dev/null +++ b/src/adf_core_python/core/agent/action/ambulance/action_rescue.py @@ -0,0 +1,15 @@ +from rcrscore.commands import AKRescue, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionRescue(Action): + def __init__(self, target_id: EntityID) -> None: + self.target_id = target_id + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKRescue(agent_id, time, self.target_id) + + def __str__(self) -> str: + return f"ActionRescue(target_id={self.target_id})" diff --git a/src/adf_core_python/core/agent/action/ambulance/action_unload.py b/src/adf_core_python/core/agent/action/ambulance/action_unload.py new file mode 100644 index 00000000..4c464589 --- /dev/null +++ b/src/adf_core_python/core/agent/action/ambulance/action_unload.py @@ -0,0 +1,12 @@ +from rcrscore.commands import AKUnload, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionUnload(Action): + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKUnload(agent_id, time) + + def __str__(self) -> str: + return "ActionUnload()" diff --git a/src/adf_core_python/core/agent/action/common/__init__.py b/src/adf_core_python/core/agent/action/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/common/action_move.py b/src/adf_core_python/core/agent/action/common/action_move.py new file mode 100644 index 00000000..b6ddac0a --- /dev/null +++ b/src/adf_core_python/core/agent/action/common/action_move.py @@ -0,0 +1,37 @@ +from typing import Optional + +from rcrscore.commands import AKMove, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionMove(Action): + def __init__( + self, + path: list[EntityID], + destination_x: Optional[int] = None, + destination_y: Optional[int] = None, + ) -> None: + self.path = path + self.destination_x = destination_x + self.destination_y = destination_y + + def is_destination_defined(self) -> bool: + return self.destination_x is not None and self.destination_y is not None + + def get_destination_x(self) -> Optional[int]: + return self.destination_x + + def get_destination_y(self) -> Optional[int]: + return self.destination_y + + def get_command(self, agent_id: EntityID, time: int) -> Command: + if self.destination_x is not None and self.destination_y is not None: + return AKMove(agent_id, time, self.path, self.destination_x, self.destination_y) + else: + return AKMove(agent_id, time, self.path) + + def __str__(self) -> str: + path: str = ", ".join([str(p) for p in self.path]) + return f"ActionMove(path={path}, destination_x={self.destination_x}, destination_y={self.destination_y})" diff --git a/src/adf_core_python/core/agent/action/common/action_rest.py b/src/adf_core_python/core/agent/action/common/action_rest.py new file mode 100644 index 00000000..1d61001d --- /dev/null +++ b/src/adf_core_python/core/agent/action/common/action_rest.py @@ -0,0 +1,12 @@ +from rcrscore.commands import AKRest, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionRest(Action): + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKRest(agent_id, time) + + def __str__(self) -> str: + return "ActionRest()" diff --git a/src/adf_core_python/core/agent/action/fire/__init__.py b/src/adf_core_python/core/agent/action/fire/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/fire/action_extinguish.py b/src/adf_core_python/core/agent/action/fire/action_extinguish.py new file mode 100644 index 00000000..e14a6635 --- /dev/null +++ b/src/adf_core_python/core/agent/action/fire/action_extinguish.py @@ -0,0 +1,16 @@ +from rcrscore.commands import AKExtinguish, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionExtinguish(Action): + def __init__(self, target_id: EntityID, max_power: int) -> None: + self.target_id = target_id + self.max_power = max_power + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKExtinguish(agent_id, time, self.target_id, self.max_power) + + def __str__(self) -> str: + return f"ActionExtinguish(target_id={self.target_id}, max_power={self.max_power})" diff --git a/src/adf_core_python/core/agent/action/fire/action_refill.py b/src/adf_core_python/core/agent/action/fire/action_refill.py new file mode 100644 index 00000000..8d11578e --- /dev/null +++ b/src/adf_core_python/core/agent/action/fire/action_refill.py @@ -0,0 +1,12 @@ +from rcrscore.commands import AKRest, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionRefill(Action): + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKRest(agent_id, time) + + def __str__(self) -> str: + return "ActionRefill()" diff --git a/src/adf_core_python/core/agent/action/fire/action_rescue.py b/src/adf_core_python/core/agent/action/fire/action_rescue.py new file mode 100644 index 00000000..8e3ce84d --- /dev/null +++ b/src/adf_core_python/core/agent/action/fire/action_rescue.py @@ -0,0 +1,15 @@ +from rcrscore.commands import AKRescue, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionRescue(Action): + def __init__(self, target_id: EntityID) -> None: + self.target_id = target_id + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKRescue(agent_id, time, self.target_id) + + def __str__(self) -> str: + return f"ActionRescue(target_id={self.target_id})" diff --git a/src/adf_core_python/core/agent/action/police/__init__.py b/src/adf_core_python/core/agent/action/police/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/action/police/action_clear.py b/src/adf_core_python/core/agent/action/police/action_clear.py new file mode 100644 index 00000000..2709b728 --- /dev/null +++ b/src/adf_core_python/core/agent/action/police/action_clear.py @@ -0,0 +1,15 @@ +from rcrscore.commands import AKClear, Command +from rcrscore.entities import Blockade, EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionClear(Action): + def __init__(self, blockade: Blockade) -> None: + self.blockade = blockade + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKClear(agent_id, time, self.blockade.get_entity_id()) + + def __str__(self) -> str: + return f"ActionClear(blockade={self.blockade})" diff --git a/src/adf_core_python/core/agent/action/police/action_clear_area.py b/src/adf_core_python/core/agent/action/police/action_clear_area.py new file mode 100644 index 00000000..41dbc632 --- /dev/null +++ b/src/adf_core_python/core/agent/action/police/action_clear_area.py @@ -0,0 +1,24 @@ +from rcrscore.commands import AKClearArea, Command +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.action.action import Action + + +class ActionClearArea(Action): + def __init__(self, position_x: int, position_y: int) -> None: + self.position_x = position_x + self.position_y = position_y + + def get_position_x(self) -> int: + return self.position_x + + def get_position_y(self) -> int: + return self.position_y + + def get_command(self, agent_id: EntityID, time: int) -> Command: + return AKClearArea(agent_id, time, self.position_x, self.position_y) + + def __str__(self) -> str: + return ( + f"ActionClearArea(position_x={self.position_x}, position_y={self.position_y})" + ) diff --git a/src/adf_core_python/core/agent/agent.py b/src/adf_core_python/core/agent/agent.py new file mode 100644 index 00000000..f96dd588 --- /dev/null +++ b/src/adf_core_python/core/agent/agent.py @@ -0,0 +1,386 @@ +import sys +import time as _time +from abc import abstractmethod +from threading import Event +from typing import Any, Callable, NoReturn, Optional + +from bitarray import bitarray +from rcrscore.commands import ( + AKClear, + AKClearArea, + AKLoad, + AKMove, + AKRescue, + AKRest, + AKSay, + AKSpeak, + AKSubscribe, + AKTell, + AKUnload, + Command, +) +from rcrscore.config.config import Config as RCRSConfig + +# from rcrscore.connection.URN import Command as CommandURN +# from rcrscore.connection.URN import ComponentCommand as ComponentCommandMessageID +# from rcrscore.connection.URN import ComponentControlMSG as ComponentControlMessageID +# from rcrscore.connection.URN import Entity as EntityURN +from rcrscore.entities import EntityID + +# from rcrscore.messages.AKAcknowledge import AKAcknowledge +# from rcrscore.messages.AKConnect import AKConnect +# from rcrscore.messages.controlMessageFactory import ControlMessageFactory +# from rcrscore.messages.KAConnectError import KAConnectError +# from rcrscore.messages.KAConnectOK import KAConnectOK +# from rcrscore.messages.KASense import KASense +from rcrscore.messages import ( + AKAcknowledge, + AKConnect, + ControlMessageFactory, + KAConnectError, + KAConnectOK, + KASense, +) +from rcrscore.urn import ( + CommandURN, + ComponentCommandURN, + ComponentControlMessageURN, + EntityURN, +) +from rcrscore.worldmodel import ChangeSet, WorldModel + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.standard_communication_module import ( + StandardCommunicationModule, +) +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import Mode, ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.communication.communication_module import ( + CommunicationModule, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.logger.logger import get_agent_logger, get_logger + + +class Agent: + def __init__( + self, + is_precompute: bool, + name: str, + is_debug: bool, + team_name: str, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + self.name = name + self.connect_request_id = None + self.world_model = WorldModel() + self.config: Config + self.random = None + self.agent_id: EntityID + self.precompute_flag = is_precompute + self.logger = get_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}" + ) + self.finish_post_connect_event = finish_post_connect_event + + self.team_name = team_name + self.is_debug = is_debug + self.is_precompute = is_precompute + + if is_precompute: + self._mode = Mode.PRECOMPUTATION + + try: + self._precompute_data = PrecomputeData(data_storage_name) + except Exception as _: + pass + + self._module_config = module_config + self._develop_data = develop_data + self._message_manager: MessageManager = MessageManager() + self._communication_module: CommunicationModule = StandardCommunicationModule() + + self._gateway_agent: Optional[GatewayAgent] = gateway_agent + + def get_entity_id(self) -> EntityID: + return self.agent_id + + def set_send_msg(self, connection_send_func: Callable) -> None: + self.send_msg = connection_send_func + + def post_connect(self) -> None: + if self.is_precompute: + self._mode = Mode.PRECOMPUTATION + else: + if self._precompute_data.is_available(): + self._mode = Mode.PRECOMPUTED + else: + self._mode = Mode.NON_PRECOMPUTE + + self.config.set_value(ConfigKey.KEY_DEBUG_FLAG, self.is_debug) + self.config.set_value( + ConfigKey.KEY_DEVELOP_FLAG, self._develop_data.is_develop_mode() + ) + self._ignore_time: int = int(self.config.get_value("kernel.agents.ignoreuntil", 3)) + + self._scenario_info: ScenarioInfo = ScenarioInfo(self.config, self._mode) + self._world_info: WorldInfo = WorldInfo(self.world_model) + self._agent_info = AgentInfo(self, self.world_model) + + if isinstance(self._gateway_agent, GatewayAgent): + self._gateway_agent.set_initialize_data( + self._agent_info, self._world_info, self._scenario_info + ) + + self.logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + self.logger.debug(f"agent_config: {self.config}") + + def update_step_info( + self, time: int, change_set: ChangeSet, hear: list[Command] + ) -> None: + self._agent_info.record_think_start_time() + self._agent_info.set_time(time) + + if time == 1: + self._message_manager.register_message_class(0, MessageAmbulanceTeam) + self._message_manager.register_message_class(1, MessageFireBrigade) + self._message_manager.register_message_class(2, MessagePoliceForce) + self._message_manager.register_message_class(3, MessageBuilding) + self._message_manager.register_message_class(4, MessageCivilian) + self._message_manager.register_message_class(5, MessageRoad) + self._message_manager.register_message_class(6, CommandAmbulance) + self._message_manager.register_message_class(7, CommandFire) + self._message_manager.register_message_class(8, CommandPolice) + self._message_manager.register_message_class(9, CommandScout) + self._message_manager.register_message_class(10, MessageReport) + + if time > self._ignore_time: + self._message_manager.subscribe( + self._agent_info, self._world_info, self._scenario_info + ) + if not self._message_manager.get_is_subscribed(): + subscribed_channels = self._message_manager.get_subscribed_channels() + if subscribed_channels: + self.logger.debug( + f"Subscribed channels: {subscribed_channels}", + message_manager=self._message_manager, + ) + self.send_subscribe(time, subscribed_channels) + self._message_manager.set_is_subscribed(True) + + self._agent_info.set_heard_commands(hear) + self._agent_info.set_change_set(change_set) + self._world_info.set_change_set(change_set) + + if ( + isinstance(self._gateway_agent, GatewayAgent) + and self._gateway_agent.is_initialized() + ): + self._gateway_agent.update() + + self._message_manager.refresh() + self._communication_module.receive(self, self._message_manager) + + self.think() + + self._message_manager.coordinate_message( + self._agent_info, self._world_info, self._scenario_info + ) + self._communication_module.send(self, self._message_manager) + + @abstractmethod + def think(self) -> None: + pass + + @abstractmethod + def get_requested_entities(self) -> list[EntityURN]: + pass + + def start_up(self, request_id: int) -> None: + ak_connect = AKConnect() + self.send_msg( + ak_connect.write(request_id, self.name, self.get_requested_entities()) + ) + + def message_received(self, msg: Any) -> None: + c_msg = ControlMessageFactory().make_message(msg) + if isinstance(c_msg, KASense): + self.handler_sense(c_msg) + elif isinstance(c_msg, KAConnectOK): + self.handle_connect_ok(c_msg) + elif isinstance(c_msg, KAConnectError): + self.handle_connect_error(c_msg) + + def handle_connect_error(self, msg: Any) -> NoReturn: + if msg.reason.startswith("No more agents"): + self.logger.debug( + "Agent already connected: %s(request_id: %s)", + msg.reason, + msg.request_id, + ) + self.finish_post_connect_event.set() + else: + self.logger.error( + "Failed to connect agent: %s(request_id: %s)", + msg.reason, + msg.request_id, + ) + sys.exit(1) + + def handle_connect_ok(self, msg: Any) -> None: + self.agent_id = EntityID(msg.agent_id) + self.world_model.add_entities(msg.world) + config: RCRSConfig = msg.config + self.config = Config() + if config is not None: + for key, value in config.data.items(): + self.config.set_value(key, value) + + self.send_acknowledge(msg.request_id) + self.post_connect() + self.logger.info( + f"Connected to kernel: {self.__class__.__qualname__} (request_id: {msg.request_id}, agent_id: {self.agent_id}, mode: {self._mode})", + request_id=msg.request_id, + ) + if self.is_precompute: + self.logger.info("Precompute finished") + exit(0) + + self.finish_post_connect_event.set() + + def handler_sense(self, msg: KASense) -> None: + _id = msg.agent_id + time = msg.time + change_set = msg.change_set + heard = msg.hear.commands + + if _id != self.get_entity_id(): + self.logger.error("Agent ID mismatch: %s != %s", _id, self.get_entity_id()) + return + + heard_commands: list[Command] = [] + for heard_command in heard: + if heard_command.urn == CommandURN.AK_SPEAK: + heard_commands.append( + AKSpeak( + EntityID( + heard_command.components[ComponentControlMessageURN.AgentID].entityID + ), + heard_command.components[ComponentControlMessageURN.Time].intValue, + heard_command.components[ComponentCommandURN.Message].rawData, + heard_command.components[ComponentCommandURN.Channel].intValue, + ) + ) + self.world_model.merge(change_set) + start_update_info_time = _time.time() + self.update_step_info(time, change_set, heard_commands) + self.logger.debug( + f"{time} step calculation time: {_time.time() - start_update_info_time}" + ) + + def send_acknowledge(self, request_id: int) -> None: + ak_ack = AKAcknowledge() + self.send_msg(ak_ack.write(request_id, self.agent_id)) + + def send_clear(self, time: int, target: EntityID) -> None: + cmd = AKClear(self.get_entity_id(), time, target) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_clear_area(self, time: int, x: int = -1, y: int = -1) -> None: + cmd = AKClearArea(self.get_entity_id(), time, x, y) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_load(self, time: int, target: EntityID) -> None: + cmd = AKLoad(self.get_entity_id(), time, target) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_move( + self, time: int, path: list[EntityID], x: int = -1, y: int = -1 + ) -> None: + cmd = AKMove(self.get_entity_id(), time, path, x, y) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_rescue(self, time: int, target: EntityID) -> None: + cmd = AKRescue(self.get_entity_id(), time, target) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_rest(self, time: int) -> None: + cmd = AKRest(self.get_entity_id(), time) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_say(self, time_step: int, message: bytes) -> None: + cmd = AKSay(self.get_entity_id(), time_step, message) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_speak(self, time_step: int, message: bitarray, channel: int) -> None: + cmd = AKSpeak(self.get_entity_id(), time_step, bytes(message), channel) # type: ignore + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_subscribe(self, time: int, channels: list[int]) -> None: + cmd = AKSubscribe(self.get_entity_id(), time, channels) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_tell(self, time: int, message: bytes) -> None: + cmd = AKTell(self.get_entity_id(), time, message) + msg = cmd.to_message_proto() + self.send_msg(msg) + + def send_unload(self, time: int) -> None: + cmd = AKUnload(self.get_entity_id(), time) + msg = cmd.to_message_proto() + self.send_msg(msg) diff --git a/src/adf_core_python/core/agent/communication/__init__.py b/src/adf_core_python/core/agent/communication/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/message_manager.py b/src/adf_core_python/core/agent/communication/message_manager.py new file mode 100644 index 00000000..f66fc891 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/message_manager.py @@ -0,0 +1,371 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, ScenarioInfoKeys +from adf_core_python.core.agent.info.world_info import WorldInfo + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, + ) + from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, + ) + from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, + ) + + +class MessageManager: + MAX_MESSAGE_CLASS_COUNT = 32 + + def __init__(self) -> None: + """ + Initialize the MessageManager. + + Parameters + ---------- + __standard_message_class_count : int + The count of standard message classes. + __custom_message_class_count : int + The count of custom message classes. + __message_classes : dict[int, CommunicationMessage] + The message classes. + __subscribed_channels : list[int] + The subscribed channels. Default is [1]. + __is_subscribed : bool + The flag to indicate if the agent is subscribed to the channel. + """ + self.__standard_message_class_count = 0b0000_0001 + self.__custom_message_class_count = 0b0001_0000 + self.__message_classes: dict[int, type[CommunicationMessage]] = {} + self.__send_message_list: list[CommunicationMessage] = [] + self.__received_message_list: list[CommunicationMessage] = [] + self.__channel_send_message_list: list[list[CommunicationMessage]] = [] + self.__check_duplicate_cache: set[int] = set() + self.__message_coordinator: MessageCoordinator + self.__channel_subscriber: ChannelSubscriber + self.__heard_agent_help_message_count: int = 0 + self.__subscribed_channels: list[int] = [1] + self.__is_subscribed = False + + def set_subscribed_channels(self, subscribed_channels: list[int]) -> None: + """ + Set the subscribed channels. + + Parameters + ---------- + subscribed_channels : list[int] + The subscribed channels. + + """ + self.__subscribed_channels = subscribed_channels + self.__is_subscribed = False + + def get_subscribed_channels(self) -> list[int]: + """ + Get the subscribed channels. + + Returns + ------- + list[int] + The subscribed channels. + + """ + return self.__subscribed_channels + + def set_is_subscribed(self, is_subscribed: bool) -> None: + """ + Set the flag to indicate if the agent is subscribed to the channel. + + Parameters + ---------- + is_subscribed : bool + The flag to indicate if the agent is subscribed to the channel. + + """ + self.__is_subscribed = is_subscribed + + def get_is_subscribed(self) -> bool: + """ + Get the flag to indicate if the agent is subscribed to the channel. + + Returns + ------- + bool + The flag to indicate if the agent is subscribed to the channel. + + """ + return self.__is_subscribed + + def set_channel_subscriber(self, channel_subscriber: ChannelSubscriber) -> None: + """ + Set the channel subscriber. + + Parameters + ---------- + channel_subscriber : ChannelSubscriber + The channel subscriber. + + """ + self.__channel_subscriber = channel_subscriber + + def set_message_coordinator(self, message_coordinator: MessageCoordinator) -> None: + """ + Set the message coordinator. + + Parameters + ---------- + message_coordinator : MessageCoordinator + The message coordinator. + + """ + self.__message_coordinator = message_coordinator + + def get_channel_subscriber(self) -> ChannelSubscriber: + """ + Get the channel subscriber. + + Returns + ------- + ChannelSubscriber + The channel subscriber. + + """ + return self.__channel_subscriber + + def add_heard_agent_help_message_count(self) -> None: + """ + Add the heard agent help message count. + + """ + self.__heard_agent_help_message_count += 1 + + def get_heard_agent_help_message_count(self) -> int: + """ + Get the heard agent help message count. + + Returns + ------- + int + The heard agent help message count. + + """ + return self.__heard_agent_help_message_count + + def add_received_message(self, message: CommunicationMessage) -> None: + """ + Add the received message. + + Parameters + ---------- + message : CommunicationMessage + The received message. + + """ + self.__received_message_list.append(message) + + def get_received_message_list(self) -> list[CommunicationMessage]: + """ + Get the received message list. + + Returns + ------- + list[CommunicationMessage] + The received message list. + + """ + return self.__received_message_list + + def get_channel_send_message_list(self) -> list[list[CommunicationMessage]]: + """ + Get the channel send message list. + + Returns + ------- + list[list[CommunicationMessage]] + The channel send message list. + + """ + return self.__channel_send_message_list + + def subscribe( + self, agent_info: AgentInfo, world_info: WorldInfo, scenario_info: ScenarioInfo + ) -> None: + """ + Subscribe to the channel. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + Throws + ------ + ValueError + If the ChannelSubscriber is not set. + + """ + if self.__channel_subscriber is None: + raise ValueError("ChannelSubscriber is not set.") + + if agent_info.get_time() == 1: + self.__subscribed_channels = self.__channel_subscriber.subscribe( + agent_info, world_info, scenario_info + ) + + def register_message_class( + self, index: int, message_class: type[CommunicationMessage] + ) -> None: + """ + Register the message class. + + Parameters + ---------- + message_class : type[CommunicationMessage] + The message class. + + """ + if index >= self.MAX_MESSAGE_CLASS_COUNT: + raise ValueError( + f"Possible index values are 0 to {self.MAX_MESSAGE_CLASS_COUNT - 1}" + ) + self.__message_classes[index] = message_class + + def get_message_class(self, index: int) -> type[CommunicationMessage]: + """ + Get the message class. + + Parameters + ---------- + index : int + The index. + + Returns + ------- + type[CommunicationMessage] + The message class. + + """ + return self.__message_classes[index] + + def get_message_class_index(self, message_class: type[CommunicationMessage]) -> int: + """ + Get the message class index. + + Parameters + ---------- + message_class : type[CommunicationMessage] + The message class. + + Returns + ------- + int + The message class index. + + """ + for index, cls in self.__message_classes.items(): + if cls == message_class: + return index + return -1 + + def add_message( + self, message: CommunicationMessage, check_duplicate: bool = True + ) -> None: + """ + Add the message. + + Parameters + ---------- + message : CommunicationMessage + The message. + + """ + check_key = message.__hash__() + # TODO:両方同じコードになっているが、なぜなのか調査する + if check_duplicate and check_key not in self.__check_duplicate_cache: + self.__send_message_list.append(message) + self.__check_duplicate_cache.add(check_key) + else: + self.__send_message_list.append(message) + self.__check_duplicate_cache.add(check_key) + + def get_send_message_list(self) -> list[CommunicationMessage]: + """ + Get the send message list. + + Returns + ------- + list[CommunicationMessage] + The send message list. + + """ + return self.__send_message_list + + def coordinate_message( + self, agent_info: AgentInfo, world_info: WorldInfo, scenario_info: ScenarioInfo + ) -> None: + """ + Coordinate the message. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + """ + if self.__message_coordinator is None: + raise ValueError("MessageCoordinator is not set.") + + self.__channel_send_message_list = [ + [] + for _ in range( + int(scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1)) + ) + ] + + self.__message_coordinator.coordinate( + agent_info, + world_info, + scenario_info, + self, + self.__send_message_list, + self.__channel_send_message_list, + ) + + def refresh(self) -> None: + """ + Refresh the message manager. + + """ + self.__send_message_list = [] + self.__received_message_list = [] + self.__check_duplicate_cache = set() + self.__heard_agent_help_message_count = 0 + + def __str__(self) -> str: + return ( + f"MessageManager(" + f"standard_message_class_count={self.__standard_message_class_count}, " + f"custom_message_class_count={self.__custom_message_class_count}, " + f"message_classes={self.__message_classes}, " + f"send_message_list={self.__send_message_list}, " + f"received_message_list={self.__received_message_list}, " + f"channel_send_message_list={self.__channel_send_message_list}, " + f"check_duplicate_cache={self.__check_duplicate_cache}, " + f"message_coordinator={self.__message_coordinator}, " + f"channel_subscriber={self.__channel_subscriber}, " + f"heard_agent_help_message_count={self.__heard_agent_help_message_count}, " + f"subscribed_channels={self.__subscribed_channels}, " + f"is_subscribed={self.__is_subscribed})" + ) diff --git a/src/adf_core_python/core/agent/communication/standard/__init__.py b/src/adf_core_python/core/agent/communication/standard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/__init__.py b/src/adf_core_python/core/agent/communication/standard/bundle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/__init__.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py new file mode 100644 index 00000000..6398af7b --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandAmbulance(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_RESCUE: int = 2 + ACTION_LOAD: int = 3 + ACTION_UNLOAD: int = 4 + ACTION_AUTONOMY: int = 5 + + SIZE_AMBULANCE_TEAM_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_AMBULANCE_TEAM_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandAmbulance: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandAmbulance(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py new file mode 100644 index 00000000..3dc7d022 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandFire(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_EXTINGUISH: int = 2 + ACTION_REFILL: int = 3 + ACTION_RESCUE: int = 4 + ACTION_AUTONOMY: int = 5 + + SIZE_FIRE_BRIGADE_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_FIRE_BRIGADE_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandFire: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandFire(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py new file mode 100644 index 00000000..2c70726e --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandPolice(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + ACTION_AUTONOMY: int = 3 + + SIZE_POLICE_FORCE_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_POLICE_FORCE_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandPolice: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandPolice(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py new file mode 100644 index 00000000..9c2ae380 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandScout(StandardMessage): + SIZE_AGENT_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_SCOUT_RANGE: int = 32 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + scout_range: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._scout_range: Optional[int] = scout_range + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_scout_range(self) -> Optional[int]: + return self._scout_range + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_AGENT_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._scout_range, self.SIZE_SCOUT_RANGE) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandScout: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_AGENT_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + scout_range = read_with_exist_flag(bit_array, cls.SIZE_SCOUT_RANGE) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + scout_range if scout_range is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._scout_range, + ) + ) + + def __str__(self) -> str: + return f"CommandScout(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, scout_range={self._scout_range})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py new file mode 100644 index 00000000..46d22832 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageReport(StandardMessage): + SIZE_DONE: int = 1 + SIZE_BLOADCAST: int = 1 + + def __init__( + self, + is_wireless_message: bool, + is_done: bool, + is_bloadcast: bool, + sender_entity_id: EntityID, + priority: StandardMessagePriority, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._is_done: bool = is_done + self._is_bloadcast: bool = is_bloadcast + + def is_done(self) -> bool: + return self._is_done + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._is_done, + self.SIZE_DONE, + ) + write_with_exist_flag( + bit_array, + self._is_bloadcast, + self.SIZE_BLOADCAST, + ) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> MessageReport: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + is_done = read_with_exist_flag(bit_array, cls.SIZE_DONE) == 1 + is_bloadcast = read_with_exist_flag(bit_array, cls.SIZE_BLOADCAST) == 1 + return cls( + is_wireless_message, + is_done, + is_bloadcast, + std_message.get_sender_entity_id(), + std_message.get_priority(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._is_done, + self._is_bloadcast, + ) + ) + + def __str__(self) -> str: + return f"CommandReport(done={self._is_done}, broadcast={self._is_bloadcast})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/__init__.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py new file mode 100644 index 00000000..b649c834 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import AmbulanceTeam, EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageAmbulanceTeam(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_RESCUE: int = 2 + ACTION_LOAD: int = 3 + ACTION_UNLOAD: int = 4 + + SIZE_AMBULANCE_TEAM_ENTITY_ID: int = 32 + SIZE_AMBULANCE_TEAM_HP: int = 14 + SIZE_AMBULANCE_TEAM_BURIEDNESS: int = 13 + SIZE_AMBULANCE_TEAM_DAMAGE: int = 14 + SIZE_AMBULANCE_TEAM_POSITION: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + ambulance_team: AmbulanceTeam, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._ambulance_team_entity_id: Optional[EntityID] = ambulance_team.get_entity_id() + self._ambulance_team_hp: Optional[int] = ambulance_team.get_hp() or None + self._ambulance_team_buriedness: Optional[int] = ( + ambulance_team.get_buriedness() or None + ) + self._ambulance_team_damage: Optional[int] = ambulance_team.get_damage() or None + self._ambulance_team_position: Optional[EntityID] = ( + ambulance_team.get_position() or None + ) + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_ambulance_team_entity_id(self) -> Optional[EntityID]: + return self._ambulance_team_entity_id + + def get_ambulance_team_hp(self) -> Optional[int]: + return self._ambulance_team_hp + + def get_ambulance_team_buriedness(self) -> Optional[int]: + return self._ambulance_team_buriedness + + def get_ambulance_team_damage(self) -> Optional[int]: + return self._ambulance_team_damage + + def get_ambulance_team_position(self) -> Optional[EntityID]: + return self._ambulance_team_position + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._ambulance_team_entity_id.get_value() + if self._ambulance_team_entity_id + else None, + self.SIZE_AMBULANCE_TEAM_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, self._ambulance_team_hp, self.SIZE_AMBULANCE_TEAM_HP + ) + write_with_exist_flag( + bit_array, + self._ambulance_team_buriedness, + self.SIZE_AMBULANCE_TEAM_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, self._ambulance_team_damage, self.SIZE_AMBULANCE_TEAM_DAMAGE + ) + write_with_exist_flag( + bit_array, + self._ambulance_team_position.get_value() + if self._ambulance_team_position + else None, + self.SIZE_AMBULANCE_TEAM_POSITION, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> MessageAmbulanceTeam: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + ambulance_team_id = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_ENTITY_ID + ) + ambulance_team_hp = read_with_exist_flag(bit_array, cls.SIZE_AMBULANCE_TEAM_HP) + ambulance_team_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_BURIEDNESS + ) + ambulance_team_damage = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_DAMAGE + ) + raw_ambulance_team_position = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_POSITION + ) + ambulance_team_position = ( + EntityID(raw_ambulance_team_position) + if raw_ambulance_team_position is not None + else None + ) + raw_target_entity_id = read_with_exist_flag(bit_array, cls.SIZE_TARGET_ENTITY_ID) + target_entity_id = ( + EntityID(raw_target_entity_id) + if raw_target_entity_id is not None + else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + ambulance_team = AmbulanceTeam(ambulance_team_id or -1) + ambulance_team.set_hp(ambulance_team_hp) + ambulance_team.set_buriedness(ambulance_team_buriedness) + ambulance_team.set_damage(ambulance_team_damage) + ambulance_team.set_position(ambulance_team_position) + return MessageAmbulanceTeam( + False, + ambulance_team, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._ambulance_team_entity_id, + self._ambulance_team_hp, + self._ambulance_team_buriedness, + self._ambulance_team_damage, + self._ambulance_team_position, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessageAmbulanceTeam(ambulance_team_entity_id={self._ambulance_team_entity_id}, ambulance_team_hp={self._ambulance_team_hp}, ambulance_team_buriedness={self._ambulance_team_buriedness}, ambulance_team_damage={self._ambulance_team_damage}, ambulance_team_position={self._ambulance_team_position}, target_entity_id={self._target_entity_id}, action={self._action}, ttl={self._ttl})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py new file mode 100644 index 00000000..8a909a5d --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID +from rcrscore.entities.building import Building + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageBuilding(StandardMessage): + SIZE_BUILDING_ENTITY_ID: int = 32 + SIZE_BUILDING_BROKENNESS: int = 32 + SIZE_BUILDING_FIREYNESS: int = 32 + SIZE_BUILDING_TEMPERATURE: int = 32 + + def __init__( + self, + is_wireless_message: bool, + building: Building, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._building_entity_id: Optional[EntityID] = building.get_entity_id() + self._building_brokenness: Optional[int] = building.get_brokenness() or None + self._building_fireyness: Optional[int] = building.get_fieryness() or None + self._building_temperature: Optional[int] = building.get_temperature() or None + + def get_building_entity_id(self) -> Optional[EntityID]: + return self._building_entity_id + + def get_building_brokenness(self) -> Optional[int]: + return self._building_brokenness + + def get_building_fireyness(self) -> Optional[int]: + return self._building_fireyness + + def get_building_temperature(self) -> Optional[int]: + return self._building_temperature + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._building_entity_id.get_value() if self._building_entity_id else None, + self.SIZE_BUILDING_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._building_brokenness, + self.SIZE_BUILDING_BROKENNESS, + ) + write_with_exist_flag( + bit_array, + self._building_fireyness, + self.SIZE_BUILDING_FIREYNESS, + ) + write_with_exist_flag( + bit_array, + self._building_temperature, + self.SIZE_BUILDING_TEMPERATURE, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageBuilding: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + building_id = read_with_exist_flag(bit_array, cls.SIZE_BUILDING_ENTITY_ID) + building_brokenness = read_with_exist_flag(bit_array, cls.SIZE_BUILDING_BROKENNESS) + building_fireyness = read_with_exist_flag(bit_array, cls.SIZE_BUILDING_FIREYNESS) + building_temperature = read_with_exist_flag( + bit_array, cls.SIZE_BUILDING_TEMPERATURE + ) + building = Building(building_id or -1) + building.set_brokenness(building_brokenness) + building.set_fieryness(building_fireyness) + building.set_temperature(building_temperature) + return MessageBuilding( + False, + building, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._building_entity_id, + self._building_brokenness, + self._building_fireyness, + self._building_temperature, + ) + ) + + def __str__(self) -> str: + return f"MessageBuilding(building_entity_id={self._building_entity_id}, building_brokenness={self._building_brokenness}, building_fireyness={self._building_fireyness}, building_temperature={self._building_temperature})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py new file mode 100644 index 00000000..c1918c59 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID +from rcrscore.entities.civilian import Civilian + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageCivilian(StandardMessage): + SIZE_CIVILIAN_ENTITY_ID: int = 32 + SIZE_CIVILIAN_HP: int = 14 + SIZE_CIVILIAN_BURIEDNESS: int = 13 + SIZE_CIVILIAN_DAMAGE: int = 14 + SIZE_CIVILIAN_POSITION: int = 32 + + def __init__( + self, + is_wireless_message: bool, + civilian: Civilian, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._civilian_entity_id: Optional[EntityID] = civilian.get_entity_id() + self._civilian_hp: Optional[int] = civilian.get_hp() or None + self._civilian_buriedness: Optional[int] = civilian.get_buriedness() or None + self._civilian_damage: Optional[int] = civilian.get_damage() or None + self._civilian_position: Optional[EntityID] = civilian.get_position() or None + + def get_civilian_entity_id(self) -> Optional[EntityID]: + return self._civilian_entity_id + + def get_civilian_hp(self) -> Optional[int]: + return self._civilian_hp + + def get_civilian_buriedness(self) -> Optional[int]: + return self._civilian_buriedness + + def get_civilian_damage(self) -> Optional[int]: + return self._civilian_damage + + def get_civilian_position(self) -> Optional[EntityID]: + return self._civilian_position + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._civilian_entity_id.get_value() if self._civilian_entity_id else None, + self.SIZE_CIVILIAN_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._civilian_hp, + self.SIZE_CIVILIAN_HP, + ) + write_with_exist_flag( + bit_array, + self._civilian_buriedness, + self.SIZE_CIVILIAN_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._civilian_damage, + self.SIZE_CIVILIAN_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._civilian_position.get_value() if self._civilian_position else None, + self.SIZE_CIVILIAN_POSITION, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageCivilian: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + civilian_id = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_ENTITY_ID) + civilian_hp = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_HP) + civilian_buriedness = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_BURIEDNESS) + civilian_damage = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_DAMAGE) + raw_civilian_position = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_POSITION) + civilian_position = ( + EntityID(raw_civilian_position) if raw_civilian_position else None + ) + civilian = Civilian(civilian_id or -1) + civilian.set_hp(civilian_hp) + civilian.set_buriedness(civilian_buriedness) + civilian.set_damage(civilian_damage) + civilian.set_position(civilian_position) + return MessageCivilian( + False, + civilian, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._civilian_entity_id, + self._civilian_hp, + self._civilian_buriedness, + self._civilian_damage, + self._civilian_position, + ) + ) + + def __str__(self) -> str: + return f"MessageCivilian(civilian_entity_id={self._civilian_entity_id}, civilian_hp={self._civilian_hp}, civilian_buriedness={self._civilian_buriedness}, civilian_damage={self._civilian_damage}, civilian_position={self._civilian_position})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py new file mode 100644 index 00000000..1670b136 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID, FireBrigade + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageFireBrigade(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_EXTINGUISH: int = 2 + ACTION_REFILL: int = 3 + ACTION_RESCUE: int = 4 + + SIZE_FIRE_BRIGADE_ENTITY_ID: int = 32 + SIZE_FIRE_BRIGADE_HP: int = 14 + SIZE_FIRE_BRIGADE_BURIEDNESS: int = 13 + SIZE_FIRE_BRIGADE_DAMAGE: int = 14 + SIZE_FIRE_BRIGADE_POSITION: int = 32 + SIZE_FIRE_BRIGADE_WATER: int = 14 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + fire_brigade: FireBrigade, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._fire_brigade_entity_id: Optional[EntityID] = fire_brigade.get_entity_id() + self._fire_brigade_hp: Optional[int] = fire_brigade.get_hp() or None + self._fire_brigade_buriedness: Optional[int] = fire_brigade.get_buriedness() or None + self._fire_brigade_damage: Optional[int] = fire_brigade.get_damage() or None + self._fire_brigade_position: Optional[EntityID] = ( + fire_brigade.get_position() or None + ) + self._fire_brigade_water: Optional[int] = fire_brigade.get_water() or None + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_fire_brigade_entity_id(self) -> Optional[EntityID]: + return self._fire_brigade_entity_id + + def get_fire_brigade_hp(self) -> Optional[int]: + return self._fire_brigade_hp + + def get_fire_brigade_buriedness(self) -> Optional[int]: + return self._fire_brigade_buriedness + + def get_fire_brigade_damage(self) -> Optional[int]: + return self._fire_brigade_damage + + def get_fire_brigade_position(self) -> Optional[EntityID]: + return self._fire_brigade_position + + def get_fire_brigade_water(self) -> Optional[int]: + return self._fire_brigade_water + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._fire_brigade_entity_id.get_value() + if self._fire_brigade_entity_id + else None, + self.SIZE_FIRE_BRIGADE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_hp, + self.SIZE_FIRE_BRIGADE_HP, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_buriedness, + self.SIZE_FIRE_BRIGADE_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_damage, + self.SIZE_FIRE_BRIGADE_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_position.get_value() if self._fire_brigade_position else None, + self.SIZE_FIRE_BRIGADE_POSITION, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_water, + self.SIZE_FIRE_BRIGADE_WATER, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageFireBrigade: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + fire_brigade_id = read_with_exist_flag(bit_array, cls.SIZE_FIRE_BRIGADE_ENTITY_ID) + fire_brigade_hp = read_with_exist_flag(bit_array, cls.SIZE_FIRE_BRIGADE_HP) + fire_brigade_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_BURIEDNESS + ) + fire_brigade_damage = read_with_exist_flag(bit_array, cls.SIZE_FIRE_BRIGADE_DAMAGE) + raw_fire_brigade_position = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_POSITION + ) + fire_brigade_position = ( + EntityID(raw_fire_brigade_position) if raw_fire_brigade_position else None + ) + fire_brigade_water = read_with_exist_flag(bit_array, cls.SIZE_FIRE_BRIGADE_WATER) + raw_target_entity_id = read_with_exist_flag(bit_array, cls.SIZE_TARGET_ENTITY_ID) + target_entity_id = ( + EntityID(raw_target_entity_id) if raw_target_entity_id else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + fire_brigade = FireBrigade( + fire_brigade_id or -1, + ) + fire_brigade.set_hp(fire_brigade_hp) + fire_brigade.set_buriedness(fire_brigade_buriedness) + fire_brigade.set_damage(fire_brigade_damage) + fire_brigade.set_position(fire_brigade_position) + fire_brigade.set_water(fire_brigade_water) + return MessageFireBrigade( + False, + fire_brigade, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._fire_brigade_entity_id, + self._fire_brigade_hp, + self._fire_brigade_buriedness, + self._fire_brigade_damage, + self._fire_brigade_position, + self._fire_brigade_water, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessageFireBrigade(fire_brigade_entity_id={self._fire_brigade_entity_id}, fire_brigade_hp={self._fire_brigade_hp}, fire_brigade_buriedness={self._fire_brigade_buriedness}, fire_brigade_damage={self._fire_brigade_damage}, fire_brigade_position={self._fire_brigade_position}, fire_brigade_water={self._fire_brigade_water}, target_entity_id={self._target_entity_id}, action={self._action})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py new file mode 100644 index 00000000..d6f96823 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID, PoliceForce + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessagePoliceForce(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + + SIZE_POLICE_FORCE_ENTITY_ID: int = 32 + SIZE_POLICE_FORCE_HP: int = 14 + SIZE_POLICE_FORCE_BURIEDNESS: int = 13 + SIZE_POLICE_FORCE_DAMAGE: int = 14 + SIZE_POLICE_FORCE_POSITION: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + police_force: PoliceForce, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._police_force_entity_id: Optional[EntityID] = police_force.get_entity_id() + self._police_force_hp: Optional[int] = police_force.get_hp() or None + self._police_force_buriedness: Optional[int] = police_force.get_buriedness() or None + self._police_force_damage: Optional[int] = police_force.get_damage() or None + self._police_force_position: Optional[EntityID] = ( + police_force.get_position() or None + ) + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_police_force_entity_id(self) -> Optional[EntityID]: + return self._police_force_entity_id + + def get_police_force_hp(self) -> Optional[int]: + return self._police_force_hp + + def get_police_force_buriedness(self) -> Optional[int]: + return self._police_force_buriedness + + def get_police_force_damage(self) -> Optional[int]: + return self._police_force_damage + + def get_police_force_position(self) -> Optional[EntityID]: + return self._police_force_position + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._police_force_entity_id.get_value() + if self._police_force_entity_id + else None, + self.SIZE_POLICE_FORCE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._police_force_hp, + self.SIZE_POLICE_FORCE_HP, + ) + write_with_exist_flag( + bit_array, + self._police_force_buriedness, + self.SIZE_POLICE_FORCE_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._police_force_damage, + self.SIZE_POLICE_FORCE_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._police_force_position.get_value() if self._police_force_position else None, + self.SIZE_POLICE_FORCE_POSITION, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessagePoliceForce: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + police_force_id = read_with_exist_flag(bit_array, cls.SIZE_POLICE_FORCE_ENTITY_ID) + police_force_hp = read_with_exist_flag(bit_array, cls.SIZE_POLICE_FORCE_HP) + police_force_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_BURIEDNESS + ) + police_force_damage = read_with_exist_flag(bit_array, cls.SIZE_POLICE_FORCE_DAMAGE) + raw_police_force_position = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_POSITION + ) + police_force_position = ( + EntityID(raw_police_force_position) if raw_police_force_position else None + ) + raw_target_entity_id = read_with_exist_flag(bit_array, cls.SIZE_TARGET_ENTITY_ID) + target_entity_id = ( + EntityID(raw_target_entity_id) if raw_target_entity_id else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + police_force = PoliceForce(police_force_id or -1) + police_force.set_hp(police_force_hp) + police_force.set_buriedness(police_force_buriedness) + police_force.set_damage(police_force_damage) + police_force.set_position(police_force_position) + return MessagePoliceForce( + False, + police_force, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._police_force_entity_id, + self._police_force_hp, + self._police_force_buriedness, + self._police_force_damage, + self._police_force_position, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessagePoliceForce(police_force_entity_id={self._police_force_entity_id}, police_force_hp={self._police_force_hp}, police_force_buriedness={self._police_force_buriedness}, police_force_damage={self._police_force_damage}, police_force_position={self._police_force_position}, target_entity_id={self._target_entity_id}, action={self._action})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py new file mode 100644 index 00000000..d43b9d46 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import Blockade, EntityID, Road + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageRoad(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + + SIZE_ROAD_ENTITY_ID: int = 32 + SIZE_ROAD_BLOCKADE_ENTITY_ID: int = 32 + SIZE_ROAD_BLOCKADE_REPAIR_COST: int = 32 + SIZE_ROAD_BLOCKADE_X: int = 32 + SIZE_ROAD_BLOCKADE_Y: int = 32 + SIZE_PASSABLE: int = 1 + + def __init__( + self, + is_wireless_message: bool, + road: Road, + is_send_blockade_location: bool, + is_passable: Optional[bool], + blockade: Optional[Blockade], + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._road_entity_id: Optional[EntityID] = road.get_entity_id() + self._road_blockade_entity_id: Optional[EntityID] = None + self._road_blockade_repair_cost: Optional[int] = None + self._road_blockade_x: Optional[int] = None + self._road_blockade_y: Optional[int] = None + + if blockade: + self._road_blockade_entity_id = blockade.get_entity_id() + self._road_blockade_repair_cost = blockade.get_repair_cost() + if is_send_blockade_location: + self._road_blockade_x = blockade.get_x() or None + self._road_blockade_y = blockade.get_y() or None + + self._is_passable: Optional[bool] = is_passable + self._is_send_blockade_location: bool = is_send_blockade_location + + def get_road_entity_id(self) -> Optional[EntityID]: + return self._road_entity_id + + def get_road_blockade_entity_id(self) -> Optional[EntityID]: + return self._road_blockade_entity_id + + def get_road_blockade_repair_cost(self) -> Optional[int]: + return self._road_blockade_repair_cost + + def get_road_blockade_x(self) -> Optional[int]: + return self._road_blockade_x + + def get_road_blockade_y(self) -> Optional[int]: + return self._road_blockade_y + + def get_is_passable(self) -> Optional[bool]: + return self._is_passable + + def get_is_send_blockade_location(self) -> bool: + return self._is_send_blockade_location + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._road_entity_id.get_value() if self._road_entity_id else None, + self.SIZE_ROAD_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_entity_id.get_value() + if self._road_blockade_entity_id + else None, + self.SIZE_ROAD_BLOCKADE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_repair_cost if self._road_blockade_repair_cost else None, + self.SIZE_ROAD_BLOCKADE_REPAIR_COST, + ) + if self._is_send_blockade_location: + write_with_exist_flag( + bit_array, + self._road_blockade_x if self._road_blockade_x else None, + self.SIZE_ROAD_BLOCKADE_X, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_y if self._road_blockade_y else None, + self.SIZE_ROAD_BLOCKADE_Y, + ) + else: + write_with_exist_flag(bit_array, None, self.SIZE_ROAD_BLOCKADE_X) + write_with_exist_flag(bit_array, None, self.SIZE_ROAD_BLOCKADE_Y) + write_with_exist_flag( + bit_array, + self._is_passable if self._is_passable else None, + self.SIZE_PASSABLE, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageRoad: + std_message = super().from_bits(bit_array, is_wireless_message, sender_entity_id) + road_id = read_with_exist_flag(bit_array, cls.SIZE_ROAD_ENTITY_ID) + road_blockade_id = read_with_exist_flag(bit_array, cls.SIZE_ROAD_BLOCKADE_ENTITY_ID) + road_blockade_repair_cost = read_with_exist_flag( + bit_array, cls.SIZE_ROAD_BLOCKADE_REPAIR_COST + ) + road_blockade_x = read_with_exist_flag(bit_array, cls.SIZE_ROAD_BLOCKADE_X) + road_blockade_y = read_with_exist_flag(bit_array, cls.SIZE_ROAD_BLOCKADE_Y) + is_passable = True if read_with_exist_flag(bit_array, cls.SIZE_PASSABLE) else False + road = Road(road_id or -1) + blockade = Blockade(road_blockade_id or -1) + blockade.set_repair_cost(road_blockade_repair_cost) + blockade.set_x(road_blockade_x) + blockade.set_y(road_blockade_y) + return MessageRoad( + is_wireless_message, + road, + False, + is_passable, + blockade, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._road_entity_id, + self._road_blockade_entity_id, + self._road_blockade_repair_cost, + self._road_blockade_x, + self._road_blockade_y, + self._is_passable, + self._is_send_blockade_location, + ) + ) + + def __str__(self) -> str: + return f"MessageRoad(road_entity_id={self._road_entity_id}, road_blockade_entity_id={self._road_blockade_entity_id}, road_blockade_repair_cost={self._road_blockade_repair_cost}, road_blockade_x={self._road_blockade_x}, road_blockade_y={self._road_blockade_y}, is_passable={self._is_passable}, is_send_blockade_location={self._is_send_blockade_location})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/standard_message.py b/src/adf_core_python/core/agent/communication/standard/bundle/standard_message.py new file mode 100644 index 00000000..296cfd38 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/standard_message.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class StandardMessage(CommunicationMessage): + SIZE_TTL: int = 3 + + def __init__( + self, + is_wireless_message: bool, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message) + self._priority = priority + self._sender_entity_id = sender_entity_id + self._ttl = ttl + + def get_sender_entity_id(self) -> EntityID: + return self._sender_entity_id + + def get_priority(self) -> StandardMessagePriority: + return self._priority + + def get_ttl(self) -> Optional[int]: + return self._ttl + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> StandardMessage: + ttl = read_with_exist_flag(bit_array, cls.SIZE_TTL) + return StandardMessage( + is_wireless_message, + StandardMessagePriority.NORMAL, + sender_entity_id, + ttl, + ) + + def to_bits(self) -> bitarray: + bit_array = bitarray() + write_with_exist_flag(bit_array, self._ttl, self.SIZE_TTL) + return bit_array + + def get_bit_size(self) -> int: + raise NotImplementedError + + def __hash__(self) -> int: + return hash((self._sender_entity_id, self._priority, self._ttl)) + + def __str__(self) -> str: + return f"StandardMessage(sender_entity_id={self._sender_entity_id}, priority={self._priority}, ttl={self._ttl})" diff --git a/src/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py b/src/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py new file mode 100644 index 00000000..357a9ac0 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py @@ -0,0 +1,24 @@ +from enum import Enum + + +class StandardMessagePriority(Enum): + """ + Standard message priorities. + """ + + LOW = 0 + NORMAL = 1 + HIGH = 2 + + def __str__(self) -> str: + return self.name.lower() + + def __lt__(self, other: object) -> bool: + if isinstance(other, StandardMessagePriority): + return self.value < other.value + return NotImplemented + + def __le__(self, other: object) -> bool: + if isinstance(other, StandardMessagePriority): + return self.value <= other.value + return NotImplemented diff --git a/src/adf_core_python/core/agent/communication/standard/standard_communication_module.py b/src/adf_core_python/core/agent/communication/standard/standard_communication_module.py new file mode 100644 index 00000000..1a91c035 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/standard_communication_module.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from bitarray import bitarray +from rcrscore.commands import AKSpeak +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) +from adf_core_python.core.component.communication.communication_module import ( + CommunicationModule, +) +from adf_core_python.core.logger.logger import get_logger + +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + + +class StandardCommunicationModule(CommunicationModule): + ESCAPE_CHAR = bitarray("11111111") + SIZE_ID: int = 5 + SIZE_TTL: int = 3 + + def receive(self, agent: Agent, message_manager: MessageManager) -> None: + heard_commands = agent._agent_info.get_heard_commands() + for command in heard_commands: + if isinstance(command, AKSpeak): + sender_entity_id = command.agent_id + if sender_entity_id == agent.get_entity_id(): + continue + data = command.message + is_wireless_message = command.channel != 0 + + if len(data) == 0: + continue + + if is_wireless_message: + bit_array = bitarray() + bit_array.frombytes(data) + self.add_received_message( + message_manager, + is_wireless_message, + sender_entity_id, + bit_array, + ) + else: + try: + voice_message = data.decode("utf-8") + if voice_message.startswith("Help") or voice_message.startswith("Ouch"): + message_manager.add_heard_agent_help_message_count() + continue + except UnicodeDecodeError: + pass + + escape_char = self.ESCAPE_CHAR.tobytes()[0] + i = 0 + bit_array = bitarray() + a = bitarray() + a.frombytes(data) + while i < len(data): + if data[i] == escape_char: + if (i + 1) >= len(data): + self.add_received_message( + message_manager, + False, + sender_entity_id, + bit_array, + ) + break + elif data[i + 1] != escape_char: + self.add_received_message( + message_manager, + False, + sender_entity_id, + bit_array, + ) + bit_array.clear() + i += 1 # Skip the next character + continue + i += 1 # Skip the escaped character + bits = bitarray() + bits.frombytes(data[i].to_bytes(1, "big")) + bit_array.extend(bits) + i += 1 + + def send(self, agent: Agent, message_manager: MessageManager) -> None: + voice_message_limit_bytes = agent._scenario_info.get_value( + "comms.channels.0.messages.size", 256 + ) + left_voice_message_limit_bits = voice_message_limit_bytes * 8 + voice_message_bit_array = bitarray() + + send_messages = message_manager.get_channel_send_message_list() + + for channel in range(len(send_messages)): + for message in send_messages[channel]: + message_class_index = message_manager.get_message_class_index(type(message)) + bit_array = bitarray() + + write_with_exist_flag(bit_array, message_class_index, self.SIZE_ID) + + bit_array.extend(message.to_bits()) + + if channel > 0: + agent.send_speak( + agent._agent_info.get_time(), + bit_array, + channel, + ) + else: + # bit_arrayを8bitごとに区切れるようにして、エスケープ処理を行う + if len(bit_array) % 8 != 0: + bit_array.extend([False] * (8 - len(bit_array) % 8)) + message_bit_size = len(bit_array) + if message_bit_size <= left_voice_message_limit_bits: + esceped_message_data = bitarray() + for i in range(len(bit_array) // 8): + if bit_array[(i * 8) : ((i + 1) * 8)] == self.ESCAPE_CHAR: + esceped_message_data.extend(self.ESCAPE_CHAR) + esceped_message_data.extend(bit_array[(i * 8) : ((i + 1) * 8)]) + esceped_message_data.extend(self.ESCAPE_CHAR) + if len(esceped_message_data) <= voice_message_limit_bytes: + left_voice_message_limit_bits -= len(esceped_message_data) + voice_message_bit_array.extend(esceped_message_data) + + if len(voice_message_bit_array) > 0: + agent.send_speak( + agent._agent_info.get_time(), + voice_message_bit_array, + 0, + ) + + def add_received_message( + self, + message_manager: MessageManager, + is_wireless_message: bool, + sender_entity_id: EntityID, + data: bitarray, + ) -> None: + message_class_index = read_with_exist_flag(data, self.SIZE_ID) + if message_class_index is None or message_class_index < 0: + get_logger(f"{self.__class__.__module__}.{self.__class__.__qualname__}").warning( + f"Invalid message class index: {message_class_index}" + ) + return + + message = message_manager.get_message_class(message_class_index) + if message is None: + get_logger(f"{self.__class__.__module__}.{self.__class__.__qualname__}").warning( + f"Invalid message class index: {message_class_index}" + ) + return + + if issubclass(message, StandardMessage): + message_instance = message.from_bits(data, is_wireless_message, sender_entity_id) + message_manager.add_received_message(message_instance) + else: + get_logger(f"{self.__class__.__module__}.{self.__class__.__qualname__}").warning( + f"Invalid message class: {message}" + ) + return diff --git a/src/adf_core_python/core/agent/communication/standard/utility/__init__.py b/src/adf_core_python/core/agent/communication/standard/utility/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py b/src/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py new file mode 100644 index 00000000..3c0efb99 --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py @@ -0,0 +1,317 @@ +from rcrscore.entities import ( + AmbulanceTeam, + Blockade, + Building, + Civilian, + FireBrigade, + PoliceForce, + Road, +) + +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.info.world_info import WorldInfo + + +def apply_to_world_info( + world_info: WorldInfo, + standard_message: StandardMessage, +) -> None: + """ + Apply to world info. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + + if isinstance(standard_message, MessageAmbulanceTeam): + _apply_to_world_info_ambulance_team(world_info, standard_message) + elif isinstance(standard_message, MessageFireBrigade): + _apply_to_world_info_fire_brigade(world_info, standard_message) + elif isinstance(standard_message, MessagePoliceForce): + _apply_to_world_info_police_force(world_info, standard_message) + elif isinstance(standard_message, MessageCivilian): + _apply_to_world_info_civilian(world_info, standard_message) + elif isinstance(standard_message, MessageBuilding): + _apply_to_world_info_building(world_info, standard_message) + elif isinstance(standard_message, MessageRoad): + _apply_to_world_info_road(world_info, standard_message) + else: + return + + +def _apply_to_world_info_ambulance_team( + world_info: WorldInfo, + message_ambulance_team: MessageAmbulanceTeam, +) -> None: + """ + Apply to world info for ambulance team. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_ambulance_team.get_ambulance_team_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + ambulance = AmbulanceTeam(entity_id.get_value()) + if (hp := message_ambulance_team.get_ambulance_team_hp()) is not None: + ambulance.set_hp(hp) + if (damege := message_ambulance_team.get_ambulance_team_damage()) is not None: + ambulance.set_damage(damege) + if ( + buriedness := message_ambulance_team.get_ambulance_team_buriedness() + ) is not None: + ambulance.set_buriedness(buriedness) + if (position := message_ambulance_team.get_ambulance_team_position()) is not None: + ambulance.set_position(position) + world_info.add_entity(ambulance) + else: + if isinstance(entity, AmbulanceTeam): + if (hp := message_ambulance_team.get_ambulance_team_hp()) is not None: + entity.set_hp(hp) + if (damege := message_ambulance_team.get_ambulance_team_damage()) is not None: + entity.set_damage(damege) + if ( + buriedness := message_ambulance_team.get_ambulance_team_buriedness() + ) is not None: + entity.set_buriedness(buriedness) + if (position := message_ambulance_team.get_ambulance_team_position()) is not None: + entity.set_position(position) + + +def _apply_to_world_info_fire_brigade( + world_info: WorldInfo, + message_fire_brigade: MessageFireBrigade, +) -> None: + """ + Apply to world info for fire brigade. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_fire_brigade.get_fire_brigade_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + fire_brigade = FireBrigade(entity_id.get_value()) + if (hp := message_fire_brigade.get_fire_brigade_hp()) is not None: + fire_brigade.set_hp(hp) + if (damage := message_fire_brigade.get_fire_brigade_damage()) is not None: + fire_brigade.set_damage(damage) + if (buriedness := message_fire_brigade.get_fire_brigade_buriedness()) is not None: + fire_brigade.set_buriedness(buriedness) + if (position := message_fire_brigade.get_fire_brigade_position()) is not None: + fire_brigade.set_position(position) + if (water := message_fire_brigade.get_fire_brigade_water()) is not None: + fire_brigade.set_water(water) + world_info.add_entity(fire_brigade) + else: + if isinstance(entity, FireBrigade): + if (hp := message_fire_brigade.get_fire_brigade_hp()) is not None: + entity.set_hp(hp) + if (damage := message_fire_brigade.get_fire_brigade_damage()) is not None: + entity.set_damage(damage) + if (buriedness := message_fire_brigade.get_fire_brigade_buriedness()) is not None: + entity.set_buriedness(buriedness) + if (position := message_fire_brigade.get_fire_brigade_position()) is not None: + entity.set_position(position) + if (water := message_fire_brigade.get_fire_brigade_water()) is not None: + entity.set_water(water) + + +def _apply_to_world_info_police_force( + world_info: WorldInfo, + message_police_force: MessagePoliceForce, +) -> None: + """ + Apply to world info for police force. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_police_force.get_police_force_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + police_force = PoliceForce(entity_id.get_value()) + if (hp := message_police_force.get_police_force_hp()) is not None: + police_force.set_hp(hp) + if (damage := message_police_force.get_police_force_damage()) is not None: + police_force.set_damage(damage) + if (buriedness := message_police_force.get_police_force_buriedness()) is not None: + police_force.set_buriedness(buriedness) + if (position := message_police_force.get_police_force_position()) is not None: + police_force.set_position(position) + world_info.add_entity(police_force) + else: + if isinstance(entity, PoliceForce): + if (hp := message_police_force.get_police_force_hp()) is not None: + entity.set_hp(hp) + if (damage := message_police_force.get_police_force_damage()) is not None: + entity.set_damage(damage) + if (buriedness := message_police_force.get_police_force_buriedness()) is not None: + entity.set_buriedness(buriedness) + if (position := message_police_force.get_police_force_position()) is not None: + entity.set_position(position) + + +def _apply_to_world_info_civilian( + world_info: WorldInfo, + message_civilian: MessageCivilian, +) -> None: + """ + Apply to world info for civilian. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_civilian.get_civilian_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + civilian = Civilian(entity_id.get_value()) + if (hp := message_civilian.get_civilian_hp()) is not None: + civilian.set_hp(hp) + if (damage := message_civilian.get_civilian_damage()) is not None: + civilian.set_damage(damage) + if (buriedness := message_civilian.get_civilian_buriedness()) is not None: + civilian.set_buriedness(buriedness) + if (position := message_civilian.get_civilian_position()) is not None: + civilian.set_position(position) + world_info.add_entity(civilian) + else: + if isinstance(entity, Civilian): + if (hp := message_civilian.get_civilian_hp()) is not None: + entity.set_hp(hp) + if (damage := message_civilian.get_civilian_damage()) is not None: + entity.set_damage(damage) + if (buriedness := message_civilian.get_civilian_buriedness()) is not None: + entity.set_buriedness(buriedness) + if (position := message_civilian.get_civilian_position()) is not None: + entity.set_position(position) + + +def _apply_to_world_info_building( + world_info: WorldInfo, + message_building: MessageBuilding, +) -> None: + """ + Apply to world info for building. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_building.get_building_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + building = Building(entity_id.get_value()) + if (fieryness := message_building.get_building_fireyness()) is not None: + building.set_fieryness(fieryness) + if (brokenness := message_building.get_building_brokenness()) is not None: + building.set_brokenness(brokenness) + if (temperature := message_building.get_building_temperature()) is not None: + building.set_temperature(temperature) + world_info.add_entity(building) + else: + if isinstance(entity, Building): + if (fieryness := message_building.get_building_fireyness()) is not None: + entity.set_fieryness(fieryness) + if (brokenness := message_building.get_building_brokenness()) is not None: + entity.set_brokenness(brokenness) + if (temperature := message_building.get_building_temperature()) is not None: + entity.set_temperature(temperature) + + +def _apply_to_world_info_road( + world_info: WorldInfo, + message_road: MessageRoad, +) -> None: + """ + Apply to world info for road. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_road.get_road_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if not isinstance(entity, Road): + return + + blockade_entity_id = message_road.get_road_blockade_entity_id() + if blockade_entity_id is None: + return + + blockade = world_info.get_entity(blockade_entity_id) + if blockade is None: + road_blockade = Blockade(blockade_entity_id.get_value()) + if (repair_cost := message_road.get_road_blockade_repair_cost()) is not None: + road_blockade.set_repair_cost(repair_cost) + if (x := message_road.get_road_blockade_x()) is not None: + road_blockade.set_x(x) + if (y := message_road.get_road_blockade_y()) is not None: + road_blockade.set_y(y) + world_info.add_entity(road_blockade) + else: + if isinstance(blockade, Blockade): + if (repair_cost := message_road.get_road_blockade_repair_cost()) is not None: + blockade.set_repair_cost(repair_cost) + if (x := message_road.get_road_blockade_x()) is not None: + blockade.set_x(x) + if (y := message_road.get_road_blockade_y()) is not None: + blockade.set_y(y) diff --git a/src/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py b/src/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py new file mode 100644 index 00000000..ec6b3b3f --- /dev/null +++ b/src/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py @@ -0,0 +1,72 @@ +from typing import Optional + +from bitarray import bitarray + +IS_EXIST_FLAG = 1 +IS_NOT_EXIST_FLAG = 0 + + +def write_with_exist_flag( + bit_array: bitarray, value: Optional[int], bit_size: int +) -> None: + """ + Write value to bit_array with an exist flag. + If value is None, write IS_NOT_EXIST_FLAG to bit_array. + If value is not None, write IS_EXIST_FLAG to bit_array and then write value to bit_array. + + PARAMETERS + ---------- + bit_array: bitarray + The bitarray to write to. + value: Optional[int] + The value to write. + bit_size: int + The number of bits to use to write value. + + RAISES + ------ + ValueError + If value is too large to fit into bit_size bits. + """ + if value is None: + bit_array.extend([IS_NOT_EXIST_FLAG]) + else: + bit_array.extend([IS_EXIST_FLAG]) + bit_value = bitarray(f"{value:0{bit_size}b}") + if len(bit_value) > bit_size: + raise ValueError(f"Value {value} is too large to fit into {bit_size} bits") + bit_array.extend(bit_value) + + +def read_with_exist_flag(bit_array: bitarray, size: int) -> Optional[int]: + """ + Read value from bit_array with an exist flag. + If the first bit is IS_NOT_EXIST_FLAG, return None. + If the first bit is IS_EXIST_FLAG, read and return value from bit_array. + + PARAMETERS + ---------- + bit_array: bitarray + The bitarray to read from. + size: int + The number of bits to read to get value. + + RETURNS + ------- + Optional[int] + The value read from bit_array. + + RAISES + ------ + ValueError + If the first bit is not IS_EXIST_FLAG or IS_NOT_EXIST_FLAG. + """ + exist_flag = bit_array.pop(0) + if exist_flag == IS_NOT_EXIST_FLAG: + return None + elif exist_flag == IS_EXIST_FLAG: + value = int(bit_array[:size].to01(), 2) + del bit_array[:size] + return value + else: + raise ValueError("Invalid exist flag") diff --git a/src/adf_core_python/core/agent/config/__init__.py b/src/adf_core_python/core/agent/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/config/module_config.py b/src/adf_core_python/core/agent/config/module_config.py new file mode 100644 index 00000000..25ff869b --- /dev/null +++ b/src/adf_core_python/core/agent/config/module_config.py @@ -0,0 +1,90 @@ +from typing import Any, Final + +from rcrscore.config.config import Config +from yaml import safe_load + + +class ModuleConfig(Config): + DEFAULT_CONFIG_FILE_NAME: Final[str] = "config/module.yaml" + + def __init__(self, config_file_name: str = DEFAULT_CONFIG_FILE_NAME): + """ + Constructor + + Parameters + ---------- + config_file_name : str, optional + Configuration file name, by default DEFAULT_CONFIG_FILE_NAME + + Raises + ------ + FileNotFoundError + If config file not found + Exception + If error reading config file + + Examples + -------- + >>> config = ModuleConfig("config/module.yaml") + >>> config.get_value("DefaultTacticsPoliceOffice.TargetAllocator") + "sample_team.module.complex.SamplePoliceTargetAllocator" + """ + super().__init__() + data = self._read_from_yaml(config_file_name) + flatten_data = self._flatten(data) + for key, value in flatten_data.items(): + self.set_value(key, value) + + def _read_from_yaml(self, file_name: str) -> dict[str, Any]: + """ + Read configuration from yaml file + + Parameters + ---------- + file_name : str + Configuration file name + + Returns + ------- + dict[str, Any] + Configuration data + """ + try: + with open(file_name, mode="r", encoding="utf-8") as file: + data = safe_load(file) + except FileNotFoundError: + raise FileNotFoundError(f"Config file not found: {file_name}") + except Exception as e: + raise Exception(f"Error reading config file: {file_name}, {e}") + + return data + + def _flatten( + self, data: dict[str, Any], parent_key: str = "", sep: str = "." + ) -> dict[str, Any]: + """ + Flatten nested dictionary to a single level dictionary + + Parameters + ---------- + data : dict[str, Any] + Nested dictionary + parent_key : str, optional + Parent key, by default "" + sep : str, optional + Separator, by default "." + + Returns + ------- + dict[str, Any] + Flattened dictionary + """ + flatten_data: dict[str, Any] = {} + for key, value in data.items(): + new_key = f"{parent_key}{sep}{key}" if parent_key else key + if isinstance(value, dict): + v: dict[str, Any] = value + flatten_data.update(self._flatten(v, new_key, sep=sep)) + else: + flatten_data[new_key] = value + return flatten_data diff --git a/src/adf_core_python/core/agent/develop/__init__.py b/src/adf_core_python/core/agent/develop/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/develop/develop_data.py b/src/adf_core_python/core/agent/develop/develop_data.py new file mode 100644 index 00000000..35bcb138 --- /dev/null +++ b/src/adf_core_python/core/agent/develop/develop_data.py @@ -0,0 +1,70 @@ +import json +from typing import Any, Final + + +class DevelopData: + DEFAULT_FILE_NAME: Final[str] = "config/develop.json" + + def __init__( + self, is_develop_mode: bool = False, file_name: str = DEFAULT_FILE_NAME + ) -> None: + """ + Constructor + """ + self._develop_data: dict[str, Any] = self._set_data_from_json(file_name) + self._is_develop_mode: bool = is_develop_mode + + self._set_data_from_json(file_name) + + def _set_data_from_json(self, file_name: str) -> dict[str, Any]: + """ + Set data from json + + Parameters + ---------- + file_name : str, optional + Develop file name, by default DEFAULT_FILE_NAME + + Returns + ------- + dict[str, Any] + Develop data + """ + try: + with open(file_name, mode="r", encoding="utf-8") as file: + return json.load(file) + except FileNotFoundError: + raise FileNotFoundError(f"Develop file not found: {file_name}") + except Exception as e: + raise Exception(f"Error reading develop file: {file_name}, {e}") + + def is_develop_mode(self) -> bool: + """ + Check if develop mode is enabled + + Returns + ------- + bool + True if develop mode is enabled + """ + return self._is_develop_mode + + def get_value(self, key: str, default_value: Any = None) -> Any: + """ + Get value from develop data + If develop mode is disabled, return default value + + Parameters + ---------- + key : str + Key + + Returns + ------- + Any + Value + """ + if not self._is_develop_mode: + return default_value + + return self._develop_data.get(key, default_value) diff --git a/src/adf_core_python/core/agent/info/__init__.py b/src/adf_core_python/core/agent/info/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/info/agent_info.py b/src/adf_core_python/core/agent/info/agent_info.py new file mode 100644 index 00000000..20658bb6 --- /dev/null +++ b/src/adf_core_python/core/agent/info/agent_info.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +from time import time +from typing import TYPE_CHECKING + +from rcrscore.commands import Command +from rcrscore.entities import EntityID +from rcrscore.entities.civilian import Civilian +from rcrscore.entities.entity import Entity +from rcrscore.entities.human import Human +from rcrscore.worldmodel import ChangeSet, WorldModel + +from adf_core_python.core.agent.action.action import Action + +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + + +class AgentInfo: + def __init__(self, agent: Agent, world_model: WorldModel): + self._agent: Agent = agent + self._world_model: WorldModel = world_model + self._time: int = 0 + self._action_history: dict[int, Action] = {} + self._heard_commands: list[Command] = [] + self._change_set: ChangeSet = ChangeSet() + self._start_think_time: float = 0.0 + + def set_time(self, time: int) -> None: + """ + Set the current time of the agent + + Parameters + ---------- + time : int + Current time + """ + self._time = time + + def get_time(self) -> int: + """ + Get the current time of the agent + + Returns + ------- + int + Current time + """ + return self._time + + def set_heard_commands(self, heard_commands: list[Command]) -> None: + """ + Set the heard commands + + Parameters + ---------- + heard_commands : list[Command] + Heard commands + """ + self._heard_commands = heard_commands + + def get_heard_commands(self) -> list[Command]: + """ + Get the heard commands + + Returns + ------- + list[Command] + Heard commands + """ + return self._heard_commands + + def get_entity_id(self) -> EntityID: + """ + Get the entity ID of the agent + + Returns + ------- + EntityID + Entity ID of the agent + """ + # TODO: Agent class should return EntityID instead of EntityID | None + return self._agent.get_entity_id() + + def get_myself(self) -> Entity | None: + """ + Get the entity of the agent + + Returns + ------- + Entity + Entity of the agent + """ + return self._world_model.get_entity(self.get_entity_id()) + + def get_position_entity_id(self) -> EntityID | None: + """ + Get the position entity ID of the agent + + Returns + ------- + EntityID + Position entity ID of the agent + """ + entity = self._world_model.get_entity(self.get_entity_id()) + if entity is None: + return None + + if isinstance(entity, Human): + return entity.get_position() + else: + return entity.get_entity_id() + + def set_change_set(self, change_set: ChangeSet) -> None: + """ + Set the change set + + Parameters + ---------- + change_set : ChangeSet + Change set + """ + self._change_set = change_set + + def get_change_set(self) -> ChangeSet: + """ + Get the change set + + Returns + ------- + ChangeSet + Change set + """ + return self._change_set + + def some_one_on_board(self) -> Human | None: + """ + Get the human if someone is on board + + Returns + ------- + Human | None + Human if someone is on board, None otherwise + """ + entity_id: EntityID = self.get_entity_id() + for entity in self._world_model.get_entities(): + if isinstance(entity, Civilian): + if entity.get_position() == entity_id: + return entity + return None + + def get_executed_action(self, time: int) -> Action | None: + """ + Get the executed action at the given time + + Parameters + ---------- + time : int + Time + """ + return self._action_history.get(time) + + def set_executed_action(self, time: int, action: Action) -> None: + """ + Set the executed action at the given time + + Parameters + ---------- + time : int + Time + action : Action + Executed action + """ + self._action_history[time] = action + + def record_think_start_time(self) -> None: + """ + Record the start time of thinking + """ + self._start_think_time = time() + + def get_think_time(self) -> float: + """ + Get the time taken for thinking + + Returns + ------- + float + Time taken for thinking + """ + return time() - self._start_think_time diff --git a/src/adf_core_python/core/agent/info/scenario_info.py b/src/adf_core_python/core/agent/info/scenario_info.py new file mode 100644 index 00000000..6e265687 --- /dev/null +++ b/src/adf_core_python/core/agent/info/scenario_info.py @@ -0,0 +1,117 @@ +from enum import IntEnum +from typing import TypeVar + +from adf_core_python.core.config.config import Config + +T = TypeVar("T") + + +class Mode(IntEnum): + NON_PRECOMPUTE = 0 + PRECOMPUTED = 1 + PRECOMPUTATION = 2 + + +class ScenarioInfoKeys: + KERNEL_PERCEPTION = "kernel.perception" + PERCEPTION_LOS_PRECISION_HP = "perception.los.precision.hp" + CLEAR_REPAIR_RAD = "clear.repair.rad" + FIRE_TANK_REFILL_HYDRANT_RATE = "fire.tank.refill_hydrant_rate" + SCENARIO_AGENTS_PF = "scenario.agents.pf" + SCENARIO_AGENTS_FS = "scenario.agents.fs" + VOICE_MESSAGES_SIZE = "comms.channels.0.messages.size" + FIRE_TANK_REFILL_RATE = "fire.tank.refill_rate" + KERNEL_TIMESTEPS = "kernel.timesteps" + FIRE_EXTINGUISH_MAX_SUM = "fire.extinguish.max-sum" + COMMUNICATION_CHANNELS_MAX_PLATOON = "comms.channels.max.platoon" + KERNEL_AGENTS_THINK_TIME = "kernel.agents.think-time" + FIRE_TANK_MAXIMUM = "fire.tank.maximum" + CLEAR_REPAIR_RATE = "clear.repair.rate" + KERNEL_STARTUP_CONNECT_TIME = "kernel.startup.connect-time" + KERNEL_HOST = "kernel.host" + SCENARIO_AGENTS_AT = "scenario.agents.at" + PERCEPTION_LOS_MAX_DISTANCE = "perception.los.max-distance" + SCENARIO_AGENTS_FB = "scenario.agents.fb" + SCENARIO_AGENTS_PO = "scenario.agents.po" + KERNEL_COMMUNICATION_MODEL = "kernel.communication-model" + PERCEPTION_LOS_PRECISION_DAMAGE = "perception.los.precision.damage" + SCENARIO_AGENTS_AC = "scenario.agents.ac" + COMMUNICATION_CHANNELS_MAX_OFFICE = "comms.channels.max.centre" + FIRE_EXTINGUISH_MAX_DISTANCE = "fire.extinguish.max-distance" + KERNEL_AGENTS_IGNOREUNTIL = "kernel.agents.ignoreuntil" + CLEAR_REPAIR_DISTANCE = "clear.repair.distance" + COMMUNICATION_CHANNELS_COUNT = "comms.channels.count" + + +class ScenarioInfo: + def __init__(self, config: Config, mode: Mode): + """ + Constructor + + Parameters + ---------- + config : Config + Configuration + mode : Mode + Mode of the scenario + """ + self._config: Config = config + self._mode: Mode = mode + + def set_config(self, config: Config) -> None: + """ + Set the configuration + + Parameters + ---------- + config : Config + Configuration + """ + self._config = config + + def get_config(self) -> Config: + """ + Get the configuration + + Returns + ------- + Config + Configuration + """ + return self._config + + def get_mode(self) -> Mode: + """ + Get the mode of the scenario + + Returns + ------- + Mode + Mode of the scenario + """ + return self._mode + + def get_value(self, key: str, default: T) -> T: + """ + Get the value of the configuration + + Parameters + ---------- + key : str + Key of the configuration + default : Any + Default value of the configuration + + Returns + ------- + Any + Value of the configuration + """ + value = self._config.get_value(key, default) + if not isinstance(value, type(default)): + try: + return type(default)(value) # type: ignore + except (ValueError, TypeError): + # 型変換に失敗した場合はそのままデフォルト値を返す + return default + return value diff --git a/src/adf_core_python/core/agent/info/world_info.py b/src/adf_core_python/core/agent/info/world_info.py new file mode 100644 index 00000000..0fce3a70 --- /dev/null +++ b/src/adf_core_python/core/agent/info/world_info.py @@ -0,0 +1,212 @@ +from typing import Any, Optional + +from rcrscore.entities import EntityID +from rcrscore.entities.area import Area +from rcrscore.entities.blockade import Blockade +from rcrscore.entities.entity import Entity +from rcrscore.entities.human import Human +from rcrscore.worldmodel import ChangeSet, WorldModel + + +class WorldInfo: + def __init__(self, world_model: WorldModel): + self._world_model: WorldModel = world_model + self._time: int = 0 + self._is_run_rollback: bool = False + self._rollback: dict[EntityID, dict[int, dict[int, Any]]] = {} + self._change_set: ChangeSet + + def get_world_model(self) -> WorldModel: + """ + Get the world model + + Returns + ------- + WorldModel + World model + """ + return self._world_model + + def set_change_set(self, change_set: ChangeSet) -> None: + """ + Set the change set + + Parameters + ---------- + change_set : ChangeSet + Change set + """ + self._change_set = change_set + + def get_entity(self, entity_id: EntityID) -> Optional[Entity]: + """ + Get the entity + + Parameters + ---------- + entity_id : EntityID + Entity ID + + Returns + ------- + Optional[Entity] + Entity + """ + return self._world_model.get_entity(entity_id) + + def get_entity_ids_of_types(self, entity_types: list[type[Entity]]) -> list[EntityID]: + """ + Get the entity IDs of the specified types + + Parameters + ---------- + entity_types : list[type[Entity]] + List of entity types + + Returns + ------- + list[EntityID] + Entity IDs + """ + entity_ids: list[EntityID] = [] + for entity in self._world_model.get_entities(): + if any(isinstance(entity, entity_type) for entity_type in entity_types): + entity_ids.append(entity.get_entity_id()) + + return entity_ids + + def get_entities_of_types(self, entity_types: list[type[Entity]]) -> list[Entity]: + """ + Get the entities of the specified types + + Parameters + ---------- + entity_types : list[type[Entity]] + List of entity types + + Returns + ------- + list[Entity] + Entities + """ + entities: list[Entity] = [] + for entity in self._world_model.get_entities(): + if any(isinstance(entity, entity_type) for entity_type in entity_types): + entities.append(entity) + + return entities + + def get_distance(self, entity_id1: EntityID, entity_id2: EntityID) -> float: + """ + Get the distance between two entities + + Parameters + ---------- + entity_id1 : EntityID + Entity ID 1 + entity_id2 : EntityID + Entity ID 2 + + Returns + ------- + float + Distance + + Raises + ------ + ValueError + If one or both entities are invalid or the location is invalid + """ + entity1: Optional[Entity] = self.get_entity(entity_id1) + entity2: Optional[Entity] = self.get_entity(entity_id2) + if entity1 is None or entity2 is None: + raise ValueError( + f"One or both entities are invalid: entity_id1={entity_id1}, entity_id2={entity_id2}, entity1={entity1}, entity2={entity2}" + ) + + location1_x, location1_y = entity1.get_x(), entity1.get_y() + location2_x, location2_y = entity2.get_x(), entity2.get_y() + if ( + location1_x is None + or location1_y is None + or location2_x is None + or location2_y is None + ): + raise ValueError( + f"Invalid location: entity_id1={entity_id1}, entity_id2={entity_id2}, location1_x={location1_x}, location1_y={location1_y}, location2_x={location2_x}, location2_y={location2_y}" + ) + + distance: float = ( + (location1_x - location2_x) ** 2 + (location1_y - location2_y) ** 2 + ) ** 0.5 + + return distance + + def get_entity_position(self, entity_id: EntityID) -> EntityID | None: + """ + Get the entity position + + Parameters + ---------- + entity_id : EntityID + Entity ID + + Returns + ------- + EntityID + Entity position + + Raises + ------ + ValueError + If the entity is invalid + """ + entity = self.get_entity(entity_id) + if entity is None: + raise ValueError(f"Invalid entity: entity_id={entity_id}, entity={entity}") + if isinstance(entity, Area): + return entity.get_entity_id() + if isinstance(entity, Human): + return entity.get_position() + if isinstance(entity, Blockade): + return entity.get_position() + raise ValueError(f"Invalid entity type: entity_id={entity_id}, entity={entity}") + + def get_change_set(self) -> ChangeSet: + """ + Get the change set + + Returns + ------- + ChangeSet + Change set + """ + return self._change_set + + def get_blockades(self, area: Area) -> set[Blockade]: + """ + Get the blockades in the area + + Returns + ------- + ChangeSet + Blockade + """ + blockades = set() + if blockade_entity_ids := area.get_blockades(): + for blockade_entity_id in blockade_entity_ids: + blockades_entity = self.get_entity(blockade_entity_id) + if isinstance(blockades_entity, Blockade): + blockades.add(blockades_entity) + return blockades + + def add_entity(self, entity: Entity) -> None: + """ + Add the entity + + Parameters + ---------- + entity : Entity + Entity + """ + self._world_model.add_entity(entity) diff --git a/src/adf_core_python/core/agent/module/__init__.py b/src/adf_core_python/core/agent/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/module/module_manager.py b/src/adf_core_python/core/agent/module/module_manager.py new file mode 100644 index 00000000..6eafb42a --- /dev/null +++ b/src/adf_core_python/core/agent/module/module_manager.py @@ -0,0 +1,269 @@ +from __future__ import annotations + +import importlib +from typing import TYPE_CHECKING, Any, Optional + +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, +) +from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, +) +from adf_core_python.core.component.module.abstract_module import AbstractModule +from adf_core_python.core.gateway.component.module.gateway_abstract_module import ( + GatewayAbstractModule, +) +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_module import GatewayModule +from adf_core_python.core.gateway.module_dict import ModuleDict +from adf_core_python.core.logger.logger import get_agent_logger + +if TYPE_CHECKING: + from adf_core_python.core.agent.config.module_config import ModuleConfig + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + + +class ModuleManager: + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_config: ModuleConfig, + develop_data: DevelopData, + gateway_agent: Optional[GatewayAgent] = None, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_config = module_config + self._develop_data = develop_data + self._gateway_agent = gateway_agent + self._modules: dict[str, AbstractModule] = {} + self._actions: dict[str, ExtendAction] = {} + + self._executors: dict[str, Any] = {} + self._pickers: dict[str, Any] = {} + self._channel_subscribers: dict[str, Any] = {} + self._message_coordinators: dict[str, Any] = {} + + self._module_dict: ModuleDict = ModuleDict() + + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def get_module( + self, module_name: str, default_module_class_name: str + ) -> AbstractModule: + instance = self._modules.get(module_name) + if instance is not None: + return instance + + class_name = self._module_config.get_value(module_name) + if class_name is None: + self._logger.warning( + f"Module {module_name} not found in config. Using default module {default_module_class_name}" + ) + + if class_name is not None: + try: + module_class: type = self._load_module(class_name) + if issubclass(module_class, AbstractModule): + instance = module_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._modules[module_name] = instance + return instance + except ModuleNotFoundError: + self._logger.warning( + f"Module {module_name} not found in python. " + f"If gateway flag is active, using module {module_name} in java" + ) + + if isinstance(self._gateway_agent, GatewayAgent): + gateway_module = GatewayModule(self._gateway_agent) + java_class_name = gateway_module.initialize(module_name, "") + class_name = self._module_dict[java_class_name] + if class_name is not None: + module_class = self._load_module(class_name) + if issubclass(module_class, GatewayAbstractModule): + instance = module_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + gateway_module, + ) + self._modules[module_name] = instance + return instance + + class_name = default_module_class_name + if class_name is not None: + module_class = self._load_module(class_name) + if issubclass(module_class, AbstractModule): + instance = module_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._modules[module_name] = instance + return instance + + raise RuntimeError(f"Module {class_name} is not a subclass of AbstractModule") + + def get_extend_action( + self, action_name: str, default_action_class_name: str + ) -> ExtendAction: + class_name = self._module_config.get_value(action_name) + if class_name is None: + self._logger.warning( + f"Action {action_name} not found in config. Using default action {default_action_class_name}" + ) + class_name = default_action_class_name + + action_class: type = self._load_module(class_name) + + instance = self._actions.get(action_name) + if instance is not None: + return instance + + if issubclass(action_class, ExtendAction): + instance = action_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._actions[action_name] = instance + return instance + + raise RuntimeError(f"Action {class_name} is not a subclass of ExtendAction") + + def get_channel_subscriber( + self, channel_subscriber_name: str, default_channel_subscriber_name: str + ) -> ChannelSubscriber: + class_name = self._module_config.get_value(channel_subscriber_name) + if class_name is None: + self._logger.warning( + f"Channel subscriber {channel_subscriber_name} not found in config. Using default channel subscriber {default_channel_subscriber_name}" + ) + class_name = default_channel_subscriber_name + + channel_subscriber_class: type = self._load_module(class_name) + + instance = self._channel_subscribers.get(channel_subscriber_name) + if instance is not None: + return instance + + if issubclass(channel_subscriber_class, ChannelSubscriber): + instance = channel_subscriber_class() + self._channel_subscribers[channel_subscriber_name] = instance + return instance + + raise RuntimeError( + f"Channel subscriber {class_name} is not a subclass of ChannelSubscriber" + ) + + def get_message_coordinator( + self, message_coordinator_name: str, default_message_coordinator_name: str + ) -> MessageCoordinator: + class_name = self._module_config.get_value(message_coordinator_name) + if class_name is None: + self._logger.warning( + f"Channel subscriber {message_coordinator_name} not found in config. Using default channel subscriber {default_message_coordinator_name}" + ) + class_name = default_message_coordinator_name + + message_coordinator_class: type = self._load_module(class_name) + + instance = self._message_coordinators.get(message_coordinator_name) + if instance is not None: + return instance + + if issubclass(message_coordinator_class, MessageCoordinator): + instance = message_coordinator_class() + self._message_coordinators[message_coordinator_name] = instance + return instance + + raise RuntimeError( + f"Message coordinator {class_name} is not a subclass of MessageCoordinator" + ) + + def get_command_executor( + self, command_executor_name: str, default_command_executor_name: str + ) -> CommandExecutor: + class_name = self._module_config.get_value(command_executor_name) + if class_name is None: + self._logger.warning( + f"Command executor {command_executor_name} not found in config. Using default command executor {default_command_executor_name}" + ) + class_name = default_command_executor_name + + command_executor_class: type = self._load_module(class_name) + + instance = self._executors.get(command_executor_name) + if instance is not None: + return instance + + if issubclass(command_executor_class, CommandExecutor): + instance = command_executor_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._executors[command_executor_name] = instance + return instance + + raise RuntimeError(f"Command executor {class_name} is not a subclass of object") + + def get_command_picker( + self, command_picker_name: str, default_command_picker_name: str + ) -> CommandPicker: + class_name = self._module_config.get_value(command_picker_name) + if class_name is None: + self._logger.warning( + f"Command picker {command_picker_name} not found in config. Using default command picker {default_command_picker_name}" + ) + class_name = default_command_picker_name + + command_picker_class: type = self._load_module(class_name) + + instance = self._pickers.get(command_picker_name) + if instance is not None: + return instance + + if issubclass(command_picker_class, CommandPicker): + instance = command_picker_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._pickers[command_picker_name] = instance + return instance + + raise RuntimeError(f"Command picker {class_name} is not a subclass of object") + + def _load_module(self, class_name: str) -> type: + module_name, module_class_name = class_name.rsplit(".", 1) + module = importlib.import_module(module_name) + return getattr(module, module_class_name) diff --git a/src/adf_core_python/core/agent/office/office.py b/src/adf_core_python/core/agent/office/office.py new file mode 100644 index 00000000..12b2a8fc --- /dev/null +++ b/src/adf_core_python/core/agent/office/office.py @@ -0,0 +1,133 @@ +from threading import Event +from typing import Optional + +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.agent import Agent +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.scenario_info import Mode +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.logger.logger import get_agent_logger + + +class Office(Agent): + def __init__( + self, + tactics_center: TacticsCenter, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + super().__init__( + is_precompute, + self.__class__.__qualname__, + is_debug, + team_name, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + self._tactics_center = tactics_center + self._team_name = team_name + self._is_precompute = is_precompute + self._is_debug = is_debug + self._data_storage_name = data_storage_name + self._module_config = module_config + self._develop_data = develop_data + + def post_connect(self) -> None: + super().post_connect() + self.precompute_data: PrecomputeData = PrecomputeData(self._data_storage_name) + + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self._module_manager: ModuleManager = ModuleManager( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_config, + self._develop_data, + ) + + self._message_manager.set_channel_subscriber( + self._module_manager.get_channel_subscriber( + "MessageManager.PlatoonChannelSubscriber", + "adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber", + ) + ) + self._message_manager.set_message_coordinator( + self._module_manager.get_message_coordinator( + "MessageManager.PlatoonMessageCoordinator", + "adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator", + ) + ) + + self._tactics_center.initialize( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + + match self._scenario_info.get_mode(): + case Mode.PRECOMPUTATION: + self._tactics_center.precompute( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + case Mode.PRECOMPUTED: + self._tactics_center.resume( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + case Mode.NON_PRECOMPUTE: + self._tactics_center.prepare( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self.precompute_data, + self._develop_data, + ) + + def think(self) -> None: + self._tactics_center.think( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + self.send_msg( + ActionRest() + .get_command(self.agent_id, self._agent_info.get_time()) + .to_message_proto() + ) diff --git a/src/adf_core_python/core/agent/office/office_ambulance.py b/src/adf_core_python/core/agent/office/office_ambulance.py new file mode 100644 index 00000000..6653faa7 --- /dev/null +++ b/src/adf_core_python/core/agent/office/office_ambulance.py @@ -0,0 +1,42 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office import Office +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class OfficeAmbulance(Office): + def __init__( + self, + tactics_center: TacticsCenter, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + super().__init__( + tactics_center, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def precompute(self) -> None: + pass + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.AMBULANCE_CENTER] diff --git a/src/adf_core_python/core/agent/office/office_fire.py b/src/adf_core_python/core/agent/office/office_fire.py new file mode 100644 index 00000000..3ec32378 --- /dev/null +++ b/src/adf_core_python/core/agent/office/office_fire.py @@ -0,0 +1,39 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office import Office +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class OfficeFire(Office): + def __init__( + self, + tactics_center: TacticsCenter, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + super().__init__( + tactics_center, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.FIRE_STATION] diff --git a/src/adf_core_python/core/agent/office/office_police.py b/src/adf_core_python/core/agent/office/office_police.py new file mode 100644 index 00000000..0aada9da --- /dev/null +++ b/src/adf_core_python/core/agent/office/office_police.py @@ -0,0 +1,39 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office import Office +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class OfficePolice(Office): + def __init__( + self, + tactics_center: TacticsCenter, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + super().__init__( + tactics_center, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.POLICE_OFFICE] diff --git a/src/adf_core_python/core/agent/platoon/__init__.py b/src/adf_core_python/core/agent/platoon/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/platoon/platoon.py b/src/adf_core_python/core/agent/platoon/platoon.py new file mode 100644 index 00000000..0f56dfd9 --- /dev/null +++ b/src/adf_core_python/core/agent/platoon/platoon.py @@ -0,0 +1,137 @@ +from threading import Event +from typing import Optional + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.agent import Agent +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.scenario_info import Mode +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.logger.logger import get_agent_logger + + +class Platoon(Agent): + def __init__( + self, + tactics_agent: TacticsAgent, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ) -> None: + super().__init__( + is_precompute, + self.__class__.__qualname__, + is_debug, + team_name, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + self._tactics_agent = tactics_agent + self._team_name = team_name + self._is_precompute = is_precompute + self._is_debug = is_debug + self._data_storage_name = data_storage_name + self._module_config = module_config + self._develop_data = develop_data + self._gateway_agent = gateway_agent + + def post_connect(self) -> None: + super().post_connect() + self.precompute_data: PrecomputeData = PrecomputeData(self._data_storage_name) + + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self._module_manager: ModuleManager = ModuleManager( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_config, + self._develop_data, + self._gateway_agent, + ) + + self._message_manager.set_channel_subscriber( + self._module_manager.get_channel_subscriber( + "MessageManager.PlatoonChannelSubscriber", + "adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber", + ) + ) + self._message_manager.set_message_coordinator( + self._module_manager.get_message_coordinator( + "MessageManager.PlatoonMessageCoordinator", + "adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator", + ) + ) + + self._tactics_agent.initialize( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + + match self._scenario_info.get_mode(): + case Mode.PRECOMPUTATION: + self._tactics_agent.precompute( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + case Mode.PRECOMPUTED: + self._tactics_agent.resume( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + case Mode.NON_PRECOMPUTE: + self._tactics_agent.prepare( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self.precompute_data, + self._develop_data, + ) + + def think(self) -> None: + action: Action = self._tactics_agent.think( + self._agent_info, + self._world_info, + self._scenario_info, + self._module_manager, + self._precompute_data, + self._message_manager, + self._develop_data, + ) + if action is not None and self.agent_id is not None: + self._agent_info.set_executed_action(self._agent_info.get_time(), action) + self.send_msg( + action.get_command( + self.agent_id, self._agent_info.get_time() + ).to_message_proto() + ) diff --git a/src/adf_core_python/core/agent/platoon/platoon_ambulance.py b/src/adf_core_python/core/agent/platoon/platoon_ambulance.py new file mode 100644 index 00000000..e55de880 --- /dev/null +++ b/src/adf_core_python/core/agent/platoon/platoon_ambulance.py @@ -0,0 +1,39 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon import Platoon +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class PlatoonAmbulance(Platoon): + def __init__( + self, + tactics_agent: TacticsAgent, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ): + super().__init__( + tactics_agent, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.AMBULANCE_TEAM] diff --git a/src/adf_core_python/core/agent/platoon/platoon_fire.py b/src/adf_core_python/core/agent/platoon/platoon_fire.py new file mode 100644 index 00000000..b5cc288e --- /dev/null +++ b/src/adf_core_python/core/agent/platoon/platoon_fire.py @@ -0,0 +1,39 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon import Platoon +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class PlatoonFire(Platoon): + def __init__( + self, + tactics_agent: TacticsAgent, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ): + super().__init__( + tactics_agent, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.FIRE_BRIGADE] diff --git a/src/adf_core_python/core/agent/platoon/platoon_police.py b/src/adf_core_python/core/agent/platoon/platoon_police.py new file mode 100644 index 00000000..8a270470 --- /dev/null +++ b/src/adf_core_python/core/agent/platoon/platoon_police.py @@ -0,0 +1,39 @@ +from threading import Event +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon import Platoon +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent +from adf_core_python.core.gateway.gateway_agent import GatewayAgent + + +class PlatoonPolice(Platoon): + def __init__( + self, + tactics_agent: TacticsAgent, + team_name: str, + is_precompute: bool, + is_debug: bool, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + finish_post_connect_event: Event, + gateway_agent: Optional[GatewayAgent], + ): + super().__init__( + tactics_agent, + team_name, + is_precompute, + is_debug, + data_storage_name, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ) + + def get_requested_entities(self) -> list[EntityURN]: + return [EntityURN.POLICE_FORCE] diff --git a/src/adf_core_python/core/agent/precompute/__init__.py b/src/adf_core_python/core/agent/precompute/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/agent/precompute/precompute_data.py b/src/adf_core_python/core/agent/precompute/precompute_data.py new file mode 100644 index 00000000..3dc33733 --- /dev/null +++ b/src/adf_core_python/core/agent/precompute/precompute_data.py @@ -0,0 +1,75 @@ +import json +import os + +ENCODE = "utf-8" + + +class PrecomputeData: + def __init__(self, dir_path: str) -> None: + """ + Initialize the PrecomputeData object. + + Parameters + ---------- + dir_path : str + The directory path to save the precompute data. + + Raises + ------ + Exception + """ + self._dir_path = dir_path + + def read_json_data(self, module_name: str) -> dict: + """ + Read the precompute data from the file. + + Returns + ------- + dict + The precompute data. + + Raises + ------ + Exception + """ + + with open(f"{self._dir_path}/{module_name}.json", "r", encoding=ENCODE) as file: + return json.load(file) + + def write_json_data(self, data: dict, module_name: str) -> None: + """ + Write the precompute data to the file. + + Parameters + ---------- + data : dict + The data to write. + + Raises + ------ + Exception + """ + if not os.path.exists(self._dir_path): + os.makedirs(self._dir_path) + + with open(f"{self._dir_path}/{module_name}.json", "w", encoding=ENCODE) as file: + json.dump(data, file, indent=4) + + def remove_precompute_data(self) -> None: + """ + Remove the precompute data file. + """ + if os.path.exists(self._dir_path): + os.remove(self._dir_path) + + def is_available(self) -> bool: + """ + Check if the precompute data is available. + + Returns + ------- + bool + True if the precompute data is available, False otherwise. + """ + return os.path.exists(self._dir_path) diff --git a/src/adf_core_python/core/component/__init__.py b/src/adf_core_python/core/component/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/abstract_loader.py b/src/adf_core_python/core/component/abstract_loader.py new file mode 100644 index 00000000..4cc859a0 --- /dev/null +++ b/src/adf_core_python/core/component/abstract_loader.py @@ -0,0 +1,53 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_ambulance_center import ( + TacticsAmbulanceCenter, +) +from adf_core_python.core.component.tactics.tactics_ambulance_team import ( + TacticsAmbulanceTeam, +) +from adf_core_python.core.component.tactics.tactics_fire_brigade import ( + TacticsFireBrigade, +) +from adf_core_python.core.component.tactics.tactics_fire_station import ( + TacticsFireStation, +) +from adf_core_python.core.component.tactics.tactics_police_force import ( + TacticsPoliceForce, +) +from adf_core_python.core.component.tactics.tactics_police_office import ( + TacticsPoliceOffice, +) + + +class AbstractLoader(ABC): + def __init__(self, team_name: Optional[str] = None): + self._team_name: str = "" if team_name is None else team_name + + def get_team_name(self) -> str: + return self._team_name + + @abstractmethod + def get_tactics_ambulance_team(self) -> TacticsAmbulanceTeam: + raise NotImplementedError + + @abstractmethod + def get_tactics_fire_brigade(self) -> TacticsFireBrigade: + raise NotImplementedError + + @abstractmethod + def get_tactics_police_force(self) -> TacticsPoliceForce: + raise NotImplementedError + + @abstractmethod + def get_tactics_ambulance_center(self) -> TacticsAmbulanceCenter: + raise NotImplementedError + + @abstractmethod + def get_tactics_fire_station(self) -> TacticsFireStation: + raise NotImplementedError + + @abstractmethod + def get_tactics_police_office(self) -> TacticsPoliceOffice: + raise NotImplementedError diff --git a/src/adf_core_python/core/component/action/__init__.py b/src/adf_core_python/core/component/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/action/extend_action.py b/src/adf_core_python/core/component/action/extend_action.py new file mode 100644 index 00000000..054f433b --- /dev/null +++ b/src/adf_core_python/core/component/action/extend_action.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.action.action import Action + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class ExtendAction(ABC): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ): + self.world_info = world_info + self.agent_info = agent_info + self.scenario_info = scenario_info + self.module_manager = module_manager + self.develop_data = develop_data + self.result: Optional[Action] = None + self.count_precompute: int = 0 + self.count_resume: int = 0 + self.count_prepare: int = 0 + self.count_update_info: int = 0 + self.count_update_info_current_time: int = 0 + + @abstractmethod + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtendAction: + raise NotImplementedError + + @abstractmethod + def calculate(self) -> ExtendAction: + raise NotImplementedError + + def get_action(self) -> Optional[Action]: + return self.result + + def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: + self.count_precompute += 1 + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtendAction: + self.count_resume += 1 + return self + + def prepare(self) -> ExtendAction: + self.count_prepare += 1 + return self + + def update_info(self, message_manager: MessageManager) -> ExtendAction: + self.count_update_info += 1 + return self + + def get_count_precompute(self) -> int: + return self.count_precompute + + def get_count_resume(self) -> int: + return self.count_resume + + def get_count_prepare(self) -> int: + return self.count_prepare + + def get_count_update_info(self) -> int: + return self.count_update_info + + def reset_count_precompute(self) -> None: + self.count_precompute = 0 + + def reset_count_resume(self) -> None: + self.count_resume = 0 + + def reset_count_prepare(self) -> None: + self.count_prepare = 0 + + def reset_count_update_info(self) -> None: + self.count_update_info = 0 diff --git a/src/adf_core_python/core/component/centralized/command_executor.py b/src/adf_core_python/core/component/centralized/command_executor.py new file mode 100644 index 00000000..6b9bd8cb --- /dev/null +++ b/src/adf_core_python/core/component/centralized/command_executor.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Generic, Optional, TypeVar + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + +T = TypeVar("T", bound=CommunicationMessage) + + +class CommandExecutor(ABC, Generic[T]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_manager = module_manager + self._develop_data = develop_data + + self._result: Optional[Action] = None + + self._count_precompute: int = 0 + self._count_prepare: int = 0 + self._count_resume: int = 0 + self._count_update_info: int = 0 + self._count_update_info_current_time: int = 0 + + @abstractmethod + def set_command(self, command: T) -> CommandExecutor: + pass + + @abstractmethod + def calculate(self) -> CommandExecutor: + pass + + def get_action(self) -> Optional[Action]: + return self._result + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + self._count_precompute += 1 + return self + + def prepare(self) -> CommandExecutor: + self._count_prepare += 1 + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + self._count_resume += 1 + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + if self._count_update_info_current_time != self._agent_info.get_time(): + self._count_update_info_current_time = self._agent_info.get_time() + self._count_update_info += 1 + return self + + def get_count_precompute(self) -> int: + return self._count_precompute + + def get_count_prepare(self) -> int: + return self._count_prepare + + def get_count_resume(self) -> int: + return self._count_resume + + def get_count_update_info(self) -> int: + return self._count_update_info + + def reset_count_precompute(self) -> None: + self._count_precompute = 0 + + def reset_count_prepare(self) -> None: + self._count_prepare = 0 + + def reset_count_resume(self) -> None: + self._count_resume = 0 + + def reset_count_update_info(self) -> None: + self._count_update_info = 0 diff --git a/src/adf_core_python/core/component/centralized/command_picker.py b/src/adf_core_python/core/component/centralized/command_picker.py new file mode 100644 index 00000000..a78c05d8 --- /dev/null +++ b/src/adf_core_python/core/component/centralized/command_picker.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +from rcrscore.entities import EntityID + +if TYPE_CHECKING: + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class CommandPicker(ABC): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_manager = module_manager + self._develop_data = develop_data + self._count_precompute: int = 0 + self._count_prepare: int = 0 + self._count_resume: int = 0 + self._count_update_info: int = 0 + self._count_update_info_current_time: int = 0 + + @abstractmethod + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + pass + + @abstractmethod + def calculate(self) -> CommandPicker: + pass + + @abstractmethod + def get_result(self) -> list[CommunicationMessage]: + pass + + def precompute(self, precompute_data: PrecomputeData) -> CommandPicker: + self._count_precompute += 1 + return self + + def prepare(self) -> CommandPicker: + self._count_prepare += 1 + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandPicker: + self._count_resume += 1 + return self + + def update_info(self, message_manager: MessageManager) -> CommandPicker: + if self._count_update_info_current_time != self._agent_info.get_time(): + self._count_update_info_current_time = self._agent_info.get_time() + self._count_update_info = 0 + self._count_update_info += 1 + return self + + def get_count_precompute(self) -> int: + return self._count_precompute + + def get_count_prepare(self) -> int: + return self._count_prepare + + def get_count_resume(self) -> int: + return self._count_resume + + def get_count_update_info(self) -> int: + return self._count_update_info + + def get_count_update_info_current_time(self) -> int: + return self._count_update_info_current_time + + def reset_count_precompute(self) -> None: + self._count_precompute = 0 + + def reset_count_prepare(self) -> None: + self._count_prepare = 0 + + def reset_count_resume(self) -> None: + self._count_resume = 0 + + def reset_count_update_info(self) -> None: + self._count_update_info = 0 diff --git a/src/adf_core_python/core/component/communication/__init__.py b/src/adf_core_python/core/component/communication/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/communication/channel_subscriber.py b/src/adf_core_python/core/component/communication/channel_subscriber.py new file mode 100644 index 00000000..9e1acd45 --- /dev/null +++ b/src/adf_core_python/core/component/communication/channel_subscriber.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + + +class ChannelSubscriber(ABC): + @abstractmethod + def subscribe( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + """ + Subscribe to the channel. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + Returns + ------- + list[int] + The list of subscribed channels. + """ + pass diff --git a/src/adf_core_python/core/component/communication/communication_message.py b/src/adf_core_python/core/component/communication/communication_message.py new file mode 100644 index 00000000..5538b407 --- /dev/null +++ b/src/adf_core_python/core/component/communication/communication_message.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + +from bitarray import bitarray + + +class CommunicationMessage(ABC): + def __init__(self, is_wireless_message: bool) -> None: + self._is_wireless_message = is_wireless_message + + def is_wireless_message(self) -> bool: + return self._is_wireless_message + + @abstractmethod + def get_bit_size(self) -> int: + raise NotImplementedError + + @abstractmethod + def to_bits(self) -> bitarray: + raise NotImplementedError + + @abstractmethod + def __hash__(self) -> int: + raise NotImplementedError diff --git a/src/adf_core_python/core/component/communication/communication_module.py b/src/adf_core_python/core/component/communication/communication_module.py new file mode 100644 index 00000000..0959f6c2 --- /dev/null +++ b/src/adf_core_python/core/component/communication/communication_module.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + from adf_core_python.core.agent.communication.message_manager import MessageManager + + +class CommunicationModule(ABC): + @abstractmethod + def receive(self, agent: Agent, message_manager: MessageManager) -> None: + pass + + @abstractmethod + def send(self, agent: Agent, message_manager: MessageManager) -> None: + pass diff --git a/src/adf_core_python/core/component/communication/message_coordinator.py b/src/adf_core_python/core/component/communication/message_coordinator.py new file mode 100644 index 00000000..86e93f1c --- /dev/null +++ b/src/adf_core_python/core/component/communication/message_coordinator.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, + ) + + +class MessageCoordinator(ABC): + @abstractmethod + def coordinate( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + message_manager: MessageManager, + send_message_list: list[CommunicationMessage], + channel_send_message_list: list[list[CommunicationMessage]], + ) -> None: + pass diff --git a/src/adf_core_python/core/component/module/__init__.py b/src/adf_core_python/core/component/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/module/abstract_module.py b/src/adf_core_python/core/component/module/abstract_module.py new file mode 100644 index 00000000..8f9e4660 --- /dev/null +++ b/src/adf_core_python/core/component/module/abstract_module.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import time +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.logger.logger import get_agent_logger + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class AbstractModule(ABC): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_manager = module_manager + self._develop_data = develop_data + self._count_precompute: int = 0 + self._count_resume: int = 0 + self._count_prepare: int = 0 + self._count_update_info: int = 0 + self._count_update_info_current_time: int = 0 + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self._sub_modules: list[AbstractModule] = [] + + def register_sub_module(self, sub_module: AbstractModule) -> None: + self._sub_modules.append(sub_module) + + def unregister_sub_module(self, sub_module: AbstractModule) -> None: + self._sub_modules.remove(sub_module) + + def precompute(self, precompute_data: PrecomputeData) -> AbstractModule: + self._count_precompute += 1 + for sub_module in self._sub_modules: + sub_module.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> AbstractModule: + self._count_resume += 1 + for sub_module in self._sub_modules: + sub_module.resume(precompute_data) + return self + + def prepare(self) -> AbstractModule: + self._count_prepare += 1 + for sub_module in self._sub_modules: + start_time = time.time() + sub_module.prepare() + self._logger.debug( + f"{self.__class__.__name__}'s sub_module {sub_module.__class__.__name__} prepare time: {time.time() - start_time:.3f}", + ) + return self + + def update_info(self, message_manager: MessageManager) -> AbstractModule: + self._count_update_info += 1 + for sub_module in self._sub_modules: + sub_module.update_info(message_manager) + return self + + @abstractmethod + def calculate(self) -> AbstractModule: + raise NotImplementedError + + def get_count_precompute(self) -> int: + return self._count_precompute + + def get_count_resume(self) -> int: + return self._count_resume + + def get_count_prepare(self) -> int: + return self._count_prepare + + def get_count_update_info(self) -> int: + return self._count_update_info + + def reset_count_precompute(self) -> None: + self._count_precompute = 0 + + def reset_count_resume(self) -> None: + self._count_resume = 0 + + def reset_count_prepare(self) -> None: + self._count_prepare = 0 + + def reset_count_update_info(self) -> None: + self._count_update_info = 0 diff --git a/src/adf_core_python/core/component/module/algorithm/__init__.py b/src/adf_core_python/core/component/module/algorithm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/module/algorithm/clustering.py b/src/adf_core_python/core/component/module/algorithm/clustering.py new file mode 100644 index 00000000..7a0331b1 --- /dev/null +++ b/src/adf_core_python/core/component/module/algorithm/clustering.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + from rcrscore.entities.entity import Entity + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class Clustering(AbstractModule): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_cluster_number(self) -> int: + pass + + @abstractmethod + def get_cluster_index(self, entity_id: EntityID) -> int: + pass + + @abstractmethod + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + pass + + @abstractmethod + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + pass + + @abstractmethod + def calculate(self) -> Clustering: + pass + + def precompute(self, precompute_data: PrecomputeData) -> Clustering: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> Clustering: + super().resume(precompute_data) + return self + + def prepare(self) -> Clustering: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> Clustering: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/module/algorithm/path_planning.py b/src/adf_core_python/core/component/module/algorithm/path_planning.py new file mode 100644 index 00000000..4a36bbbb --- /dev/null +++ b/src/adf_core_python/core/component/module/algorithm/path_planning.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class PathPlanning(AbstractModule): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_path( + self, from_entity_id: EntityID, to_entity_id: EntityID + ) -> list[EntityID]: + pass + + @abstractmethod + def get_path_to_multiple_destinations( + self, from_entity_id: EntityID, destination_entity_ids: set[EntityID] + ) -> list[EntityID]: + pass + + @abstractmethod + def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + pass + + def precompute(self, precompute_data: PrecomputeData) -> PathPlanning: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> PathPlanning: + super().resume(precompute_data) + return self + + def prepare(self) -> PathPlanning: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> PathPlanning: + super().update_info(message_manager) + return self + + @abstractmethod + def calculate(self) -> PathPlanning: + pass diff --git a/src/adf_core_python/core/component/module/complex/__init__.py b/src/adf_core_python/core/component/module/complex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/module/complex/ambulance_target_allocator.py b/src/adf_core_python/core/component/module/complex/ambulance_target_allocator.py new file mode 100644 index 00000000..8956ea9f --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/ambulance_target_allocator.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class AmbulanceTargetAllocator(TargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_result(self) -> dict[EntityID, EntityID]: + pass + + @abstractmethod + def calculate(self) -> AmbulanceTargetAllocator: + pass + + def precompute(self, precompute_data: PrecomputeData) -> AmbulanceTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> AmbulanceTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> AmbulanceTargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> AmbulanceTargetAllocator: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/module/complex/fire_target_allocator.py b/src/adf_core_python/core/component/module/complex/fire_target_allocator.py new file mode 100644 index 00000000..79d1905f --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/fire_target_allocator.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class FireTargetAllocator(TargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_result(self) -> dict[EntityID, EntityID]: + pass + + @abstractmethod + def calculate(self) -> FireTargetAllocator: + pass + + def precompute(self, precompute_data: PrecomputeData) -> FireTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> FireTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> FireTargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> FireTargetAllocator: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/module/complex/human_detector.py b/src/adf_core_python/core/component/module/complex/human_detector.py new file mode 100644 index 00000000..a0619b34 --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/human_detector.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from rcrscore.entities.human import Human + +from adf_core_python.core.component.module.complex.target_detector import ( + TargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class HumanDetector(TargetDetector[Human]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + def precompute(self, precompute_data: PrecomputeData) -> HumanDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> HumanDetector: + super().resume(precompute_data) + return self + + def prepare(self) -> HumanDetector: + super().prepare() + return self + + @abstractmethod + def calculate(self) -> HumanDetector: + return self diff --git a/src/adf_core_python/core/component/module/complex/police_target_allocator.py b/src/adf_core_python/core/component/module/complex/police_target_allocator.py new file mode 100644 index 00000000..540a60ba --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/police_target_allocator.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class PoliceTargetAllocator(TargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_result(self) -> dict[EntityID, EntityID]: + pass + + @abstractmethod + def calculate(self) -> PoliceTargetAllocator: + pass + + def precompute(self, precompute_data: PrecomputeData) -> PoliceTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> PoliceTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> PoliceTargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> PoliceTargetAllocator: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/module/complex/road_detector.py b/src/adf_core_python/core/component/module/complex/road_detector.py new file mode 100644 index 00000000..f742b2de --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/road_detector.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from rcrscore.entities.road import Road + +from adf_core_python.core.component.module.complex.target_detector import ( + TargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class RoadDetector(TargetDetector[Road]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + def precompute(self, precompute_data: PrecomputeData) -> RoadDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> RoadDetector: + super().resume(precompute_data) + return self + + def prepare(self) -> RoadDetector: + super().prepare() + return self + + @abstractmethod + def calculate(self) -> RoadDetector: + return self diff --git a/src/adf_core_python/core/component/module/complex/search.py b/src/adf_core_python/core/component/module/complex/search.py new file mode 100644 index 00000000..59c4fde8 --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/search.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from rcrscore.entities.area import Area + +from adf_core_python.core.component.module.complex.target_detector import ( + TargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class Search(TargetDetector[Area]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + def precompute(self, precompute_data: PrecomputeData) -> Search: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> Search: + super().resume(precompute_data) + return self + + def prepare(self) -> Search: + super().prepare() + return self + + @abstractmethod + def calculate(self) -> Search: + return self diff --git a/src/adf_core_python/core/component/module/complex/target_allocator.py b/src/adf_core_python/core/component/module/complex/target_allocator.py new file mode 100644 index 00000000..6979966a --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/target_allocator.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class TargetAllocator(AbstractModule): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_result(self) -> dict[EntityID, EntityID]: + pass + + @abstractmethod + def calculate(self) -> TargetAllocator: + pass + + def precompute(self, precompute_data: PrecomputeData) -> TargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> TargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> TargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> TargetAllocator: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/module/complex/target_detector.py b/src/adf_core_python/core/component/module/complex/target_detector.py new file mode 100644 index 00000000..7e7285db --- /dev/null +++ b/src/adf_core_python/core/component/module/complex/target_detector.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING, Generic, Optional, TypeVar + +from rcrscore.entities.entity import Entity + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from rcrscore.entities import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + +T = TypeVar("T", bound=Entity) + + +class TargetDetector(AbstractModule, Generic[T]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_target_entity_id(self) -> Optional[EntityID]: + pass + + @abstractmethod + def calculate(self) -> TargetDetector[T]: + pass + + def precompute(self, precompute_data: PrecomputeData) -> TargetDetector[T]: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> TargetDetector[T]: + super().resume(precompute_data) + return self + + def prepare(self) -> TargetDetector[T]: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> TargetDetector[T]: + super().update_info(message_manager) + return self diff --git a/src/adf_core_python/core/component/tactics/__init__.py b/src/adf_core_python/core/component/tactics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/component/tactics/tactics_agent.py b/src/adf_core_python/core/component/tactics/tactics_agent.py new file mode 100644 index 00000000..9502a2cc --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_agent.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import time +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Optional + +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.logger.logger import get_agent_logger + +if TYPE_CHECKING: + from adf_core_python.core.agent.action.action import Action + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.component.action.extend_action import ExtendAction + from adf_core_python.core.component.module.abstract_module import AbstractModule + + +class TacticsAgent(ABC): + def __init__(self, parent: Optional[TacticsAgent] = None) -> None: + self._parent = parent + self._modules: list[AbstractModule] = [] + self._actions: list[ExtendAction] = [] + self._command_executor: list[CommandExecutor] = [] + + @abstractmethod + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", agent_info + ) + + @abstractmethod + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> Action: + raise NotImplementedError + + def get_parent_tactics(self) -> Optional[TacticsAgent]: + return self._parent + + def register_module(self, module: AbstractModule) -> None: + self._modules.append(module) + + def unregister_module(self, module: AbstractModule) -> None: + self._modules.remove(module) + + def register_action(self, action: ExtendAction) -> None: + self._actions.append(action) + + def unregister_action(self, action: ExtendAction) -> None: + self._actions.remove(action) + + def register_command_executor(self, command_executor: CommandExecutor) -> None: + self._command_executor.append(command_executor) + + def unregister_command_executor(self, command_executor: CommandExecutor) -> None: + self._command_executor.remove(command_executor) + + def module_precompute(self, precompute_data: PrecomputeData) -> None: + for module in self._modules: + module.precompute(precompute_data) + for action in self._actions: + action.precompute(precompute_data) + for executor in self._command_executor: + executor.precompute(precompute_data) + + def module_resume(self, precompute_data: PrecomputeData) -> None: + for module in self._modules: + module.resume(precompute_data) + for action in self._actions: + action.resume(precompute_data) + for executor in self._command_executor: + executor.resume(precompute_data) + + def module_prepare(self) -> None: + for module in self._modules: + start_time = time.time() + module.prepare() + self._logger.debug( + f"module {module.__class__.__name__} prepare time: {time.time() - start_time:.3f}", + ) + for action in self._actions: + start_time = time.time() + action.prepare() + self._logger.debug( + f"module {action.__class__.__name__} prepare time: {time.time() - start_time:.3f}", + ) + for executor in self._command_executor: + executor.prepare() + + def module_update_info(self, message_manager: MessageManager) -> None: + for module in self._modules: + module.update_info(message_manager) + for action in self._actions: + action.update_info(message_manager) + for executor in self._command_executor: + executor.update_info(message_manager) + + def reset_count(self) -> None: + for module in self._modules: + module.reset_count_precompute() + module.reset_count_resume() + module.reset_count_prepare() + module.reset_count_update_info() + for action in self._actions: + action.reset_count_precompute() + action.reset_count_resume() + action.reset_count_prepare() + action.reset_count_update_info() + for executor in self._command_executor: + executor.reset_count_precompute() + executor.reset_count_resume() + executor.reset_count_prepare() + executor.reset_count_update_info() diff --git a/src/adf_core_python/core/component/tactics/tactics_ambulance_center.py b/src/adf_core_python/core/component/tactics/tactics_ambulance_center.py new file mode 100644 index 00000000..04eba005 --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_ambulance_center.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter + + +class TacticsAmbulanceCenter(TacticsCenter): + def __init__(self, parent: Optional[TacticsAmbulanceCenter] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/component/tactics/tactics_ambulance_team.py b/src/adf_core_python/core/component/tactics/tactics_ambulance_team.py new file mode 100644 index 00000000..5e2257bd --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_ambulance_team.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent + + +class TacticsAmbulanceTeam(TacticsAgent): + def __init__(self, parent: Optional[TacticsAmbulanceTeam] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/component/tactics/tactics_center.py b/src/adf_core_python/core/component/tactics/tactics_center.py new file mode 100644 index 00000000..3e35bdea --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_center.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, Optional + +from adf_core_python.core.component.centralized.command_picker import CommandPicker + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.component.module.abstract_module import AbstractModule + + +class TacticsCenter(ABC): + def __init__(self, parent: Optional[TacticsCenter] = None) -> None: + self._parent = parent + self._modules: list[AbstractModule] = [] + self._command_pickers: list[CommandPicker] = [] + + @abstractmethod + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + @abstractmethod + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + raise NotImplementedError + + def get_parent_tactics(self) -> Optional[TacticsCenter]: + return self._parent + + def register_module(self, module: AbstractModule) -> None: + self._modules.append(module) + + def unregister_module(self, module: AbstractModule) -> None: + self._modules.remove(module) + + def register_command_picker(self, command_picker: Any) -> None: + self._command_pickers.append(command_picker) + + def unregister_command_picker(self, command_picker: Any) -> None: + self._command_pickers.remove(command_picker) + + def module_precompute(self, precompute_data: PrecomputeData) -> None: + for module in self._modules: + module.precompute(precompute_data) + for command_picker in self._command_pickers: + command_picker.precompute(precompute_data) + + def module_resume(self, precompute_data: PrecomputeData) -> None: + for module in self._modules: + module.resume(precompute_data) + for command_picker in self._command_pickers: + command_picker.resume(precompute_data) + + def module_prepare(self) -> None: + for module in self._modules: + module.prepare() + for command_picker in self._command_pickers: + command_picker.prepare() + + def module_update_info(self, message_manager: MessageManager) -> None: + for module in self._modules: + module.update_info(message_manager) + for command_picker in self._command_pickers: + command_picker.update_info(message_manager) diff --git a/src/adf_core_python/core/component/tactics/tactics_fire_brigade.py b/src/adf_core_python/core/component/tactics/tactics_fire_brigade.py new file mode 100644 index 00000000..75bc6b65 --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_fire_brigade.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent + + +class TacticsFireBrigade(TacticsAgent): + def __init__(self, parent: Optional[TacticsFireBrigade] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/component/tactics/tactics_fire_station.py b/src/adf_core_python/core/component/tactics/tactics_fire_station.py new file mode 100644 index 00000000..acd25636 --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_fire_station.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter + + +class TacticsFireStation(TacticsCenter): + def __init__(self, parent: Optional[TacticsFireStation] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/component/tactics/tactics_police_force.py b/src/adf_core_python/core/component/tactics/tactics_police_force.py new file mode 100644 index 00000000..750e6f29 --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_police_force.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent + + +class TacticsPoliceForce(TacticsAgent): + def __init__(self, parent: Optional[TacticsPoliceForce] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/component/tactics/tactics_police_office.py b/src/adf_core_python/core/component/tactics/tactics_police_office.py new file mode 100644 index 00000000..a47debda --- /dev/null +++ b/src/adf_core_python/core/component/tactics/tactics_police_office.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Optional + +from adf_core_python.core.component.tactics.tactics_center import TacticsCenter + + +class TacticsPoliceOffice(TacticsCenter): + def __init__(self, parent: Optional[TacticsPoliceOffice] = None) -> None: + super().__init__(parent) diff --git a/src/adf_core_python/core/config/__init__.py b/src/adf_core_python/core/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/config/config.py b/src/adf_core_python/core/config/config.py new file mode 100644 index 00000000..a2f98a7f --- /dev/null +++ b/src/adf_core_python/core/config/config.py @@ -0,0 +1,43 @@ +from typing import Any, Optional + +from yaml import safe_load + + +class Config: + def __init__(self, config_file: Optional[str] = None) -> None: + self.config: dict[str, Any] = {} + if config_file: + self.config = self.read_from_yaml(config_file) + self.config = self.flatten(self.config) + + def set_value(self, key: str, value: Any) -> None: + self.config[key] = value + + def get_value(self, key: str, default: Any = None) -> Any: + return self.config.get(key, default) + + def read_from_yaml(self, file_name: str) -> dict[str, Any]: + try: + with open(file_name, mode="r", encoding="utf-8") as file: + data = safe_load(file) + except FileNotFoundError: + raise FileNotFoundError(f"Config file not found: {file_name}") + except Exception as e: + raise Exception(f"Error reading config file: {file_name}, {e}") + + return data + + def flatten( + self, data: dict[str, Any], parent_key: str = "", sep: str = "." + ) -> dict[str, Any]: + flatten_data = {} + for key, value in data.items(): + new_key = f"{parent_key}{sep}{key}" if parent_key else key + if isinstance(value, dict): + flatten_data.update(self.flatten(value, new_key, sep=sep)) + else: + flatten_data[new_key] = value + return flatten_data + + def __str__(self) -> str: + return str(self.config) diff --git a/src/adf_core_python/core/gateway/__init__.py b/src/adf_core_python/core/gateway/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/component/__init__.py b/src/adf_core_python/core/gateway/component/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/component/module/__init__.py b/src/adf_core_python/core/gateway/component/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/component/module/algorithm/__init__.py b/src/adf_core_python/core/gateway/component/module/algorithm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/component/module/algorithm/gateway_clustering.py b/src/adf_core_python/core/gateway/component/module/algorithm/gateway_clustering.py new file mode 100644 index 00000000..8f0c9021 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/algorithm/gateway_clustering.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING + +from rcrscore.entities import EntityID + +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.gateway.component.module.gateway_abstract_module import ( + GatewayAbstractModule, +) + +if TYPE_CHECKING: + from rcrscore.entities.entity import Entity + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayClustering(GatewayAbstractModule, Clustering): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayClustering: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayClustering: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayClustering: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayClustering: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayClustering: + super().calculate() + return self + + def get_cluster_number(self) -> int: + result = self._gateway_module.execute("getClusterNumber") + return int(result.get_value("ClusterNumber") or 0) + + def get_cluster_index(self, entity_id: EntityID) -> int: + arguments: dict[str, str] = {"EntityID": str(entity_id.get_value())} + result = self._gateway_module.execute("getClusterIndex(EntityID)", arguments) + return int(result.get_value("ClusterIndex") or 0) + + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + arguments: dict[str, str] = {"Index": str(cluster_index)} + result = self._gateway_module.execute("getClusterEntities(int)", arguments) + json_str = result.get_value("EntityIDs") or "[]" + entity_ids: list[int] = json.loads(json_str) + entities: list[Entity] = [] + for entity_id in entity_ids: + entity = self._world_info.get_entity(EntityID(entity_id)) + if entity is not None: + entities.append(entity) + return entities + + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + arguments: dict[str, str] = {"Index": str(cluster_index)} + result = self._gateway_module.execute("getClusterEntityIDs(int)", arguments) + json_str = result.get_value("EntityIDs") or "[]" + raw_entity_ids: list[int] = json.loads(json_str) + entity_ids: list[EntityID] = [] + for entity_id in raw_entity_ids: + entity_ids.append(EntityID(entity_id)) + return entity_ids diff --git a/src/adf_core_python/core/gateway/component/module/algorithm/gateway_path_planning.py b/src/adf_core_python/core/gateway/component/module/algorithm/gateway_path_planning.py new file mode 100644 index 00000000..76a146df --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/algorithm/gateway_path_planning.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING + +from rcrscore.entities import EntityID + +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +from adf_core_python.core.gateway.component.module.gateway_abstract_module import ( + GatewayAbstractModule, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayPathPlanning(GatewayAbstractModule, PathPlanning): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayPathPlanning: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayPathPlanning: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayPathPlanning: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayPathPlanning: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayPathPlanning: + super().calculate() + return self + + def get_path( + self, from_entity_id: EntityID, to_entity_id: EntityID + ) -> list[EntityID]: + arguments: dict[str, str] = { + "From": str(from_entity_id.get_value()), + "To": str(to_entity_id.get_value()), + } + result = self._gateway_module.execute("getResult(EntityID, EntityID)", arguments) + json_str = result.get_value("Result") or "[]" + raw_entity_ids: list[int] = json.loads(json_str) + entity_ids: list[EntityID] = [] + for entity_id in raw_entity_ids: + entity_ids.append(EntityID(entity_id)) + return entity_ids + + def get_path_to_multiple_destinations( + self, from_entity_id: EntityID, destination_entity_ids: set[EntityID] + ) -> list[EntityID]: + arguments: dict[str, str] = { + "From": str(from_entity_id.get_value()), + "Destinations": json.dumps( + [entity_id.get_value() for entity_id in destination_entity_ids] + ), + } + result = self._gateway_module.execute( + "getResult(EntityID, List[EntityID])", arguments + ) + json_str = result.get_value("Result") or "[]" + raw_entity_ids: list[int] = json.loads(json_str) + entity_ids: list[EntityID] = [] + for entity_id in raw_entity_ids: + entity_ids.append(EntityID(entity_id)) + return entity_ids + + def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + arguments: dict[str, str] = { + "From": str(from_entity_id.get_value()), + "To": str(to_entity_id.get_value()), + } + result = self._gateway_module.execute("getDistance(EntityID, EntityID)", arguments) + return float(result.get_value("Result") or 0.0) diff --git a/src/adf_core_python/core/gateway/component/module/complex/__init__.py b/src/adf_core_python/core/gateway/component/module/complex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_ambulance_target_allocator.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_ambulance_target_allocator.py new file mode 100644 index 00000000..fcd49024 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_ambulance_target_allocator.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.ambulance_target_allocator import ( + AmbulanceTargetAllocator, +) +from adf_core_python.core.gateway.component.module.complex.gateway_target_allocator import ( + GatewayTargetAllocator, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayAmbulanceTargetAllocator(GatewayTargetAllocator, AmbulanceTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute( + self, precompute_data: PrecomputeData + ) -> GatewayAmbulanceTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayAmbulanceTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayAmbulanceTargetAllocator: + super().prepare() + return self + + def update_info( + self, message_manager: MessageManager + ) -> GatewayAmbulanceTargetAllocator: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayAmbulanceTargetAllocator: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_fire_target_allocator.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_fire_target_allocator.py new file mode 100644 index 00000000..c3b4a780 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_fire_target_allocator.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.fire_target_allocator import ( + FireTargetAllocator, +) +from adf_core_python.core.gateway.component.module.complex.gateway_target_allocator import ( + GatewayTargetAllocator, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayFireTargetAllocator(GatewayTargetAllocator, FireTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayFireTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayFireTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayFireTargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayFireTargetAllocator: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayFireTargetAllocator: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_human_detector.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_human_detector.py new file mode 100644 index 00000000..41b1520e --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_human_detector.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rcrscore.entities.human import Human + +from adf_core_python.core.component.module.complex.human_detector import ( + HumanDetector, +) +from adf_core_python.core.gateway.component.module.complex.gateway_target_detector import ( + GatewayTargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayHumanDetector(GatewayTargetDetector[Human], HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayHumanDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayHumanDetector: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayHumanDetector: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayHumanDetector: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayHumanDetector: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_police_target_allocator.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_police_target_allocator.py new file mode 100644 index 00000000..a9ea7808 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_police_target_allocator.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.police_target_allocator import ( + PoliceTargetAllocator, +) +from adf_core_python.core.gateway.component.module.complex.gateway_target_allocator import ( + GatewayTargetAllocator, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayPoliceTargetAllocator(GatewayTargetAllocator, PoliceTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayPoliceTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayPoliceTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayPoliceTargetAllocator: + super().prepare() + return self + + def update_info( + self, message_manager: MessageManager + ) -> GatewayPoliceTargetAllocator: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayPoliceTargetAllocator: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_road_detector.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_road_detector.py new file mode 100644 index 00000000..e5b478a3 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_road_detector.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rcrscore.entities.road import Road + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.component.module.complex.road_detector import RoadDetector +from adf_core_python.core.gateway.component.module.complex.gateway_target_detector import ( + GatewayTargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayRoadDetector(GatewayTargetDetector[Road], RoadDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayRoadDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayRoadDetector: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayRoadDetector: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayRoadDetector: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayRoadDetector: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_search.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_search.py new file mode 100644 index 00000000..083f3cc5 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_search.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.gateway.component.module.complex.gateway_target_detector import ( + GatewayTargetDetector, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewaySearch(GatewayTargetDetector, Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewaySearch: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewaySearch: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewaySearch: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewaySearch: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewaySearch: + super().calculate() + return self diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_target_allocator.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_target_allocator.py new file mode 100644 index 00000000..1765c474 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_target_allocator.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rcrscore.entities import EntityID + +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) +from adf_core_python.core.gateway.component.module.gateway_abstract_module import ( + GatewayAbstractModule, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayTargetAllocator(GatewayAbstractModule, TargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayTargetAllocator: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayTargetAllocator: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayTargetAllocator: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayTargetAllocator: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayTargetAllocator: + super().calculate() + return self + + def get_result(self) -> dict[EntityID, EntityID]: + response = self._gateway_module.execute("getResult") + response_keys = response.data.keys() + result: dict[EntityID, EntityID] = {} + for key in response_keys: + value = response.get_value(key) + result[EntityID(int(key))] = EntityID(int(value if value is not None else "-1")) + + return result diff --git a/src/adf_core_python/core/gateway/component/module/complex/gateway_target_detector.py b/src/adf_core_python/core/gateway/component/module/complex/gateway_target_detector.py new file mode 100644 index 00000000..4de8f366 --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/complex/gateway_target_detector.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Generic, Optional, TypeVar + +from rcrscore.entities import EntityID +from rcrscore.entities.entity import Entity + +from adf_core_python.core.component.module.complex.target_detector import TargetDetector +from adf_core_python.core.gateway.component.module.gateway_abstract_module import ( + GatewayAbstractModule, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + +T = TypeVar("T", bound=Entity) + + +class GatewayTargetDetector(GatewayAbstractModule, TargetDetector, Generic[T]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, + world_info, + scenario_info, + module_manager, + develop_data, + gateway_module, + ) + + def precompute(self, precompute_data: PrecomputeData) -> GatewayTargetDetector[T]: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayTargetDetector[T]: + super().resume(precompute_data) + return self + + def prepare(self) -> GatewayTargetDetector[T]: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> GatewayTargetDetector[T]: + super().update_info(message_manager) + return self + + def calculate(self) -> GatewayTargetDetector[T]: + super().calculate() + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + result = self._gateway_module.execute("getTarget") + entity_id_str = result.get_value("EntityID") or "-1" + if entity_id_str == "-1": + return None + return EntityID(int(entity_id_str)) diff --git a/src/adf_core_python/core/gateway/component/module/gateway_abstract_module.py b/src/adf_core_python/core/gateway/component/module/gateway_abstract_module.py new file mode 100644 index 00000000..0f740a0a --- /dev/null +++ b/src/adf_core_python/core/gateway/component/module/gateway_abstract_module.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayAbstractModule(AbstractModule): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + gateway_module: GatewayModule, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._gateway_module = gateway_module + + def precompute(self, precompute_data: PrecomputeData) -> GatewayAbstractModule: + super().precompute(precompute_data) + self._gateway_module.execute("precompute") + return self + + def resume(self, precompute_data: PrecomputeData) -> GatewayAbstractModule: + super().resume(precompute_data) + self._gateway_module.execute("resume") + return self + + def prepare(self) -> GatewayAbstractModule: + super().prepare() + self._gateway_module.execute("preparate") + return self + + def update_info(self, message_manager: MessageManager) -> GatewayAbstractModule: + super().update_info(message_manager) + self._gateway_module.execute("updateInfo") + return self + + def calculate(self) -> GatewayAbstractModule: + self._gateway_module.execute("calc") + return self diff --git a/src/adf_core_python/core/gateway/gateway_agent.py b/src/adf_core_python/core/gateway/gateway_agent.py new file mode 100644 index 00000000..7e886d7e --- /dev/null +++ b/src/adf_core_python/core/gateway/gateway_agent.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Optional + +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn import CommandURN + +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.gateway.message.am_agent import AMAgent +from adf_core_python.core.gateway.message.am_update import AMUpdate +from adf_core_python.core.gateway.message.ma_exec_response import MAExecResponse +from adf_core_python.core.gateway.message.ma_module_response import ( + MAModuleResponse, +) +from adf_core_python.core.gateway.message.moduleMessageFactory import ( + ModuleMessageFactory, +) +from adf_core_python.core.logger.logger import get_logger + +if TYPE_CHECKING: + from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher + from adf_core_python.core.gateway.gateway_module import GatewayModule + + +class GatewayAgent: + def __init__(self, gateway_launcher: GatewayLauncher) -> None: + self._gateway_launcher = gateway_launcher + self.send_msg: Optional[Callable] = None + self._is_initialized = False + self._agent_info: Optional[AgentInfo] = None + self._world_info: Optional[WorldInfo] = None + self._scenario_info: Optional[ScenarioInfo] = None + self._gateway_modules: dict[str, GatewayModule] = {} + self._logger = get_logger(__name__) + + def get_module_count(self) -> int: + return len(self._gateway_modules) + + def add_gateway_module(self, gateway_module: GatewayModule) -> None: + self._gateway_modules[gateway_module.get_module_id()] = gateway_module + + def is_initialized(self) -> bool: + return self._is_initialized + + def set_initialize_data( + self, agent_info: AgentInfo, world_info: WorldInfo, scenario_info: ScenarioInfo + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + + def initialize(self) -> None: + if self.send_msg is None: + raise RuntimeError("send_msg is None") + if ( + self._agent_info is None + or self._world_info is None + or self._scenario_info is None + ): + raise RuntimeError( + "Required variables is None, " + "You must exec set_initialized_data() before calling initialize()" + ) + + am_agent = AMAgent() + self.send_msg( + am_agent.write( + self._agent_info.get_entity_id(), + list(self._world_info.get_world_model().get_entities()), + self._scenario_info.get_config().config, + int(self._scenario_info.get_mode()), + ) + ) + self._is_initialized = True + + def update(self) -> None: + if self.send_msg is None: + raise RuntimeError("send_msg is None") + if self._agent_info is None or self._world_info is None: + raise RuntimeError( + "Required variables is None, " + "You must exec set_initialized_data() before calling update()" + ) + + am_update = AMUpdate() + self.send_msg( + am_update.write( + self._agent_info.get_time(), + self._world_info.get_change_set(), + self._agent_info.get_heard_commands(), + ) + ) + + def set_send_msg(self, connection_send_func: Callable) -> None: + self.send_msg = connection_send_func + + def message_received(self, msg: RCRSProto_pb2.MessageProto) -> None: + c_msg = ModuleMessageFactory().make_message(msg) + if isinstance(c_msg, MAModuleResponse): + if c_msg.module_id is None or c_msg.class_name is None: + raise RuntimeError("Failed to receive message") + + self._gateway_modules[c_msg.module_id].set_gateway_class_name(c_msg.class_name) + self._gateway_modules[c_msg.module_id].set_is_initialized(True) + if isinstance(c_msg, MAExecResponse): + if c_msg.module_id is None: + raise RuntimeError("Failed to receive message") + + self._gateway_modules[c_msg.module_id].set_execute_response(c_msg.result) + self._gateway_modules[c_msg.module_id].set_is_executed(True) + + if msg.urn == CommandURN.AK_SPEAK: + if self.send_msg is None: + raise RuntimeError("send_msg is None") + self.send_msg(msg) diff --git a/src/adf_core_python/core/gateway/gateway_launcher.py b/src/adf_core_python/core/gateway/gateway_launcher.py new file mode 100644 index 00000000..47053c60 --- /dev/null +++ b/src/adf_core_python/core/gateway/gateway_launcher.py @@ -0,0 +1,45 @@ +import socket + +from structlog import BoundLogger + +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.launcher.connect.connection import Connection + + +class GatewayLauncher: + def __init__(self, host: str, port: int, logger: BoundLogger) -> None: + self.host = host + self.port = port + self.logger = logger + pass + + def make_connection(self) -> Connection: + return Connection(self.host, self.port) + + def connect(self, gateway_agent: GatewayAgent) -> None: + self.logger.info( + f"{gateway_agent.__class__.__name__} connecting to {self.host}:{self.port}" + ) + connection = self.make_connection() + try: + connection.connect() + # ソケットが使用しているPORT番号を取得 + if connection.socket is not None: + self.logger.info( + f"Connected to {self.host}:{self.port} on port {connection.socket.getsockname()[1]}" + ) + except socket.timeout: + self.logger.warning(f"Connection to {self.host}:{self.port} timed out") + return + except socket.error as e: + self.logger.error(f"Failed to connect to {self.host}:{self.port}") + self.logger.error(e) + return + + connection.message_received(gateway_agent.message_received) + gateway_agent.set_send_msg(connection.send_msg) + + try: + connection.parse_message_from_kernel() + except Exception as e: + self.logger.error(f"Failed to connect agent: {self.host}:{self.port} {e}") diff --git a/src/adf_core_python/core/gateway/gateway_module.py b/src/adf_core_python/core/gateway/gateway_module.py new file mode 100644 index 00000000..a5fa18d0 --- /dev/null +++ b/src/adf_core_python/core/gateway/gateway_module.py @@ -0,0 +1,83 @@ +import uuid +from typing import Optional + +from rcrscore.config.config import Config + +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.message.am_exec import AMExec +from adf_core_python.core.gateway.message.am_module import AMModule + + +class GatewayModule: + def __init__(self, gateway_agent: GatewayAgent): + self._gateway_agent = gateway_agent + self._module_id: str = str(uuid.uuid4()) + self._is_initialized = False + self._is_executed = False + self._gateway_class_name: str = "" + self._result: Optional[Config] = None + self._gateway_agent.add_gateway_module(self) + + def get_module_id(self) -> str: + return self._module_id + + def get_gateway_class_name(self) -> str: + return self._gateway_class_name + + def set_gateway_class_name(self, gateway_class_name: str) -> None: + self._gateway_class_name = gateway_class_name + + def get_is_initialized(self) -> bool: + return self._is_initialized + + def set_is_initialized(self, is_initialized: bool) -> None: + self._is_initialized = is_initialized + + def initialize(self, module_name: str, default_class_name: str) -> str: + if not self._gateway_agent.is_initialized(): + self._gateway_agent.initialize() + if self._gateway_agent.send_msg is None: + raise RuntimeError("send_msg is None") + + am_module = AMModule() + self._gateway_agent.send_msg( + am_module.write( + self._module_id, + module_name, + default_class_name, + ) + ) + + while not self.get_is_initialized(): + pass + + return self.get_gateway_class_name() + + def get_execute_response(self) -> Config: + if self._result is None: + raise RuntimeError("No execution result available") + return self._result + + def set_execute_response(self, result: Config) -> None: + self._result = result + + def get_is_executed(self) -> bool: + return self._is_executed + + def set_is_executed(self, _is_executed: bool) -> None: + self._is_executed = _is_executed + + def execute(self, method_name: str, args: Optional[dict[str, str]] = None) -> Config: + if args is None: + args = {} + if self._gateway_agent.send_msg is None: + raise RuntimeError("send_msg is None") + + am_exec = AMExec() + self._gateway_agent.send_msg(am_exec.write(self._module_id, method_name, args)) + + while not self.get_is_executed(): + pass + + self.set_is_executed(False) + return self.get_execute_response() diff --git a/src/adf_core_python/core/gateway/message/__init__.py b/src/adf_core_python/core/gateway/message/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/message/am_agent.py b/src/adf_core_python/core/gateway/message/am_agent.py new file mode 100644 index 00000000..416f7021 --- /dev/null +++ b/src/adf_core_python/core/gateway/message/am_agent.py @@ -0,0 +1,52 @@ +from typing import Any + +from rcrscore.entities import EntityID +from rcrscore.entities.entity import Entity +from rcrscore.messages import AKControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class AMAgent(AKControlMessage): + @staticmethod + def write( + agent_id: EntityID, + entities: list[Entity], + config: dict[str, Any], + mode: int, + ) -> RCRSProto_pb2.MessageProto: + entity_proto_list = [] + for entity in entities: + entity_proto = RCRSProto_pb2.EntityProto() + entity_proto.urn = entity.get_urn() + entity_proto.entityID = entity.get_entity_id().get_value() + + property_proto_list = [] + for k, v in entity.get_properties().items(): + property_proto_list.append(v.to_property_proto()) + entity_proto.properties.extend(property_proto_list) + entity_proto_list.append(entity_proto) + + entity_list_proto = RCRSProto_pb2.EntityListProto() + entity_list_proto.entities.extend(entity_proto_list) + + config_proto = RCRSProto_pb2.ConfigProto() + for key, value in config.items(): + config_proto.data[str(key)] = str(value) + + msg = RCRSProto_pb2.MessageProto() + msg.urn = AMAgent.get_urn() + msg.components[ComponentModuleMSG.AgentID].entityID = agent_id.get_value() + msg.components[ComponentModuleMSG.Entities].entityList.CopyFrom(entity_list_proto) + msg.components[ComponentModuleMSG.Config].config.CopyFrom(config_proto) + msg.components[ComponentModuleMSG.Mode].intValue = mode + return msg + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.AM_AGENT # type: ignore diff --git a/src/adf_core_python/core/gateway/message/am_exec.py b/src/adf_core_python/core/gateway/message/am_exec.py new file mode 100644 index 00000000..51f61d03 --- /dev/null +++ b/src/adf_core_python/core/gateway/message/am_exec.py @@ -0,0 +1,29 @@ +from typing import Any + +from rcrscore.messages import AKControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class AMExec(AKControlMessage): + @staticmethod + def write(module_id: str, method_name: str, arguments: dict[str, str]) -> Any: + msg = RCRSProto_pb2.MessageProto() + msg.urn = AMExec.get_urn() + msg.components[ComponentModuleMSG.ModuleID].stringValue = module_id + msg.components[ComponentModuleMSG.MethodName].stringValue = method_name + config_proto = RCRSProto_pb2.ConfigProto() + for key, value in arguments.items(): + config_proto.data[key] = value + msg.components[ComponentModuleMSG.Arguments].config.CopyFrom(config_proto) + + return msg + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.AM_EXEC # type: ignore diff --git a/src/adf_core_python/core/gateway/message/am_module.py b/src/adf_core_python/core/gateway/message/am_module.py new file mode 100644 index 00000000..9b2208f0 --- /dev/null +++ b/src/adf_core_python/core/gateway/message/am_module.py @@ -0,0 +1,30 @@ +from typing import Any + +from rcrscore.messages import AKControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class AMModule(AKControlMessage): + @staticmethod + def write( + module_id: str, + module_name: str, + default_class_name: str, + ) -> Any: + msg = RCRSProto_pb2.MessageProto() + msg.urn = AMModule.get_urn() + msg.components[ComponentModuleMSG.ModuleID].stringValue = module_id + msg.components[ComponentModuleMSG.ModuleName].stringValue = module_name + msg.components[ComponentModuleMSG.DefaultClassName].stringValue = default_class_name + + return msg + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.AM_MODULE # type: ignore diff --git a/src/adf_core_python/core/gateway/message/am_update.py b/src/adf_core_python/core/gateway/message/am_update.py new file mode 100644 index 00000000..c5bf941c --- /dev/null +++ b/src/adf_core_python/core/gateway/message/am_update.py @@ -0,0 +1,35 @@ +from typing import Any + +from rcrscore.commands import Command +from rcrscore.messages import AKControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN +from rcrscore.worldmodel import ChangeSet + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class AMUpdate(AKControlMessage): + @staticmethod + def write(time: int, changed: ChangeSet, heard: list[Command]) -> Any: + msg = RCRSProto_pb2.MessageProto() + msg.urn = AMUpdate.get_urn() + msg.components[ComponentModuleMSG.Time].intValue = time + msg.components[ComponentModuleMSG.Changed].changeSet.CopyFrom( + changed.to_change_set_proto() + ) + message_list_proto = RCRSProto_pb2.MessageListProto() + message_proto_list = [] + if heard is not None: + for h in heard: + message_proto_list.append(h.to_message_proto()) + message_list_proto.commands.extend(message_proto_list) + msg.components[ComponentModuleMSG.Heard].commandList.CopyFrom(message_list_proto) + return msg + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.AM_UPDATE # type: ignore diff --git a/src/adf_core_python/core/gateway/message/ma_exec_response.py b/src/adf_core_python/core/gateway/message/ma_exec_response.py new file mode 100644 index 00000000..98a50e2b --- /dev/null +++ b/src/adf_core_python/core/gateway/message/ma_exec_response.py @@ -0,0 +1,27 @@ +from rcrscore.config.config import Config +from rcrscore.messages import KAControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class MAExecResponse(KAControlMessage): + def __init__(self, message_proto: RCRSProto_pb2.MessageProto) -> None: + self.result = Config() + self.read(message_proto) + + def read(self, message_proto: RCRSProto_pb2.MessageProto) -> None: + self.module_id: str = message_proto.components[ + ComponentModuleMSG.ModuleID + ].stringValue + result = message_proto.components[ComponentModuleMSG.Result].config + for key, value in result.data.items(): + self.result.set_value(key, value) + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.MA_EXEC_RESPONSE # type: ignore diff --git a/src/adf_core_python/core/gateway/message/ma_module_response.py b/src/adf_core_python/core/gateway/message/ma_module_response.py new file mode 100644 index 00000000..ce74646e --- /dev/null +++ b/src/adf_core_python/core/gateway/message/ma_module_response.py @@ -0,0 +1,26 @@ +from abc import ABC +from typing import Optional + +from rcrscore.messages import KAControlMessage +from rcrscore.proto import RCRSProto_pb2 +from rcrscore.urn.control_message import ControlMessageURN + +from adf_core_python.core.gateway.message.urn.urn import ( + ComponentModuleMSG, + ModuleMSG, +) + + +class MAModuleResponse(KAControlMessage, ABC): + def __init__(self, message_proto: RCRSProto_pb2.MessageProto) -> None: + self.module_id: Optional[str] = None + self.class_name: Optional[str] = None + self.read(message_proto) + + def read(self, message_proto: RCRSProto_pb2.MessageProto) -> None: + self.module_id = message_proto.components[ComponentModuleMSG.ModuleID].stringValue + self.class_name = message_proto.components[ComponentModuleMSG.ClassName].stringValue + + @staticmethod + def get_urn() -> ControlMessageURN: + return ModuleMSG.MA_MODULE_RESPONSE # type: ignore diff --git a/src/adf_core_python/core/gateway/message/moduleMessageFactory.py b/src/adf_core_python/core/gateway/message/moduleMessageFactory.py new file mode 100644 index 00000000..d5a93ed8 --- /dev/null +++ b/src/adf_core_python/core/gateway/message/moduleMessageFactory.py @@ -0,0 +1,24 @@ +from typing import Optional + +from rcrscore.proto import RCRSProto_pb2 + +from adf_core_python.core.gateway.message.ma_exec_response import MAExecResponse +from adf_core_python.core.gateway.message.ma_module_response import ( + MAModuleResponse, +) +from adf_core_python.core.gateway.message.urn.urn import ModuleMSG + + +class ModuleMessageFactory: + def __init__(self) -> None: + pass + + def make_message( + self, msg: RCRSProto_pb2.MessageProto + ) -> Optional[MAModuleResponse | MAExecResponse]: + if msg.urn == ModuleMSG.MA_MODULE_RESPONSE: + return MAModuleResponse(msg) + elif msg.urn == ModuleMSG.MA_EXEC_RESPONSE: + return MAExecResponse(msg) + + return None diff --git a/src/adf_core_python/core/gateway/message/urn/__init__.py b/src/adf_core_python/core/gateway/message/urn/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/gateway/message/urn/urn.py b/src/adf_core_python/core/gateway/message/urn/urn.py new file mode 100644 index 00000000..36a15bf3 --- /dev/null +++ b/src/adf_core_python/core/gateway/message/urn/urn.py @@ -0,0 +1,27 @@ +from enum import IntEnum + + +class ModuleMSG(IntEnum): + AM_AGENT = 0x0301 + AM_MODULE = 0x0302 + MA_MODULE_RESPONSE = 0x0303 + AM_UPDATE = 0x0304 + AM_EXEC = 0x0305 + MA_EXEC_RESPONSE = 0x0306 + + +class ComponentModuleMSG(IntEnum): + AgentID = 0x0401 + Entities = 0x0402 + Config = 0x0403 + Mode = 0x0404 + ModuleID = 0x0405 + ModuleName = 0x0406 + DefaultClassName = 0x0407 + ClassName = 0x0408 + Time = 0x0409 + Changed = 0x040A + Heard = 0x040B + MethodName = 0x040C + Arguments = 0x040D + Result = 0x040E diff --git a/src/adf_core_python/core/gateway/module_dict.py b/src/adf_core_python/core/gateway/module_dict.py new file mode 100644 index 00000000..e814b778 --- /dev/null +++ b/src/adf_core_python/core/gateway/module_dict.py @@ -0,0 +1,32 @@ +from typing import Optional + + +class ModuleDict: + def __init__(self, module_dict: Optional[dict[str, str]] = None): + self.module_dict: dict[str, str] = { + "adf_core_python.component.module.algorithm.Clustering": "adf_core_python.core.gateway.component.module.complex.gateway_clustering.GatewayClustering", + "adf_core_python.component.module.algorithm.DynamicClustering": "adf_core_python.core.gateway.component.module.complex.gateway_clustering.GatewayClustering", + "adf_core_python.component.module.algorithm.StaticClustering": "adf_core_python.core.gateway.component.module.complex.gateway_clustering.GatewayClustering", + "adf_core_python.component.module.algorithm.PathPlanning": "adf_core_python.core.gateway.component.module.complex.gateway_path_planning.GatewayPathPlanning", + "adf_core_python.component.module.complex.TargetDetector": "adf_core_python.core.gateway.component.module.complex.gateway_target_detector.GatewayTargetDetector", + "adf_core_python.component.module.complex.HumanDetector": "adf_core_python.core.gateway.component.module.complex.gateway_human_detector.GatewayHumanDetector", + "adf_core_python.component.module.complex.RoadDetector": "adf_core_python.core.gateway.component.module.complex.gateway_road_detector.GatewayRoadDetector", + "adf_core_python.component.module.complex.Search": "adf_core_python.core.gateway.component.module.complex.gateway_search.GatewaySearch", + "adf_core_python.component.module.complex.TargetAllocator": "adf_core_python.core.gateway.component.module.complex.gateway_target_allocator.GatewayTargetAllocator", + "adf_core_python.component.module.complex.AmbulanceTargetAllocator": "adf_core_python.core.gateway.component.module.complex.gateway_ambulance_target_allocator.GatewayAmbulanceTargetAllocator", + "adf_core_python.component.module.complex.FireTargetAllocator": "adf_core_python.core.gateway.component.module.complex.gateway_fire_target_allocator.GatewayFireTargetAllocator", + "adf_core_python.component.module.complex.PoliceTargetAllocator": "adf_core_python.core.gateway.component.module.complex.gateway_fire_target_allocator.GatewayPoliceTargetAllocator", + } + if module_dict is not None: + for key, value in module_dict.items(): + self.module_dict[key] = value + + def __getitem__(self, key: str) -> Optional[str]: + if not isinstance(key, str): + raise TypeError("TypeError: Key must be a string") + return self.module_dict.get(key) + + def __setitem__(self, key: str, value: str) -> None: + if not isinstance(key, str): + raise TypeError("TypeError: Key must be a string") + self.module_dict[key] = value diff --git a/src/adf_core_python/core/launcher/__init__.py b/src/adf_core_python/core/launcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/launcher/agent_launcher.py b/src/adf_core_python/core/launcher/agent_launcher.py new file mode 100644 index 00000000..a5c119fc --- /dev/null +++ b/src/adf_core_python/core/launcher/agent_launcher.py @@ -0,0 +1,126 @@ +import importlib +import socket +import threading +from typing import Optional + +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.launcher.connect.connector_ambulance_center import ( + ConnectorAmbulanceCenter, +) +from adf_core_python.core.launcher.connect.connector_ambulance_team import ( + ConnectorAmbulanceTeam, +) +from adf_core_python.core.launcher.connect.connector_fire_brigade import ( + ConnectorFireBrigade, +) +from adf_core_python.core.launcher.connect.connector_fire_station import ( + ConnectorFireStation, +) +from adf_core_python.core.launcher.connect.connector_police_force import ( + ConnectorPoliceForce, +) +from adf_core_python.core.launcher.connect.connector_police_office import ( + ConnectorPoliceOffice, +) +from adf_core_python.core.logger.logger import get_logger + + +class AgentLauncher: + def __init__(self, config: Config): + self.config = config + self.logger = get_logger(__name__) + self.connectors: list[Connector] = [] + self.agent_thread_list: list[threading.Thread] = [] + + def init_connector(self) -> None: + loader_name, loader_class_name = self.config.get_value( + ConfigKey.KEY_LOADER_CLASS, + "adf_core_python.implement.default_loader.DefaultLoader", + ).rsplit(".", 1) + loader_module = importlib.import_module(loader_name) + self.loader: AbstractLoader = getattr( + loader_module, + loader_class_name, + )( + self.config.get_value(ConfigKey.KEY_TEAM_NAME), + ) + + self.connectors.append(ConnectorAmbulanceTeam()) + self.connectors.append(ConnectorAmbulanceCenter()) + self.connectors.append(ConnectorFireBrigade()) + self.connectors.append(ConnectorFireStation()) + self.connectors.append(ConnectorPoliceForce()) + self.connectors.append(ConnectorPoliceOffice()) + + def launch(self) -> None: + kernel_host: str = self.config.get_value(ConfigKey.KEY_KERNEL_HOST, "localhost") + kernel_port: int = self.config.get_value(ConfigKey.KEY_KERNEL_PORT, 27931) + + component_launcher: ComponentLauncher = ComponentLauncher( + kernel_host, kernel_port, self.logger + ) + timeout: int = self.config.get_value( + ConfigKey.KEY_KERNEL_TIMEOUT, + 30, + ) + if component_launcher.check_kernel_connection(timeout=timeout): + self.logger.info(f"Kernel is running (host: {kernel_host}, port: {kernel_port})") + else: + self.logger.error( + f"Kernel is not running (host: {kernel_host}, port: {kernel_port})" + ) + return + + self.logger.info(f"Start agent launcher (host: {kernel_host}, port: {kernel_port})") + + gateway_launcher: Optional[GatewayLauncher] = None + gateway_flag: bool = self.config.get_value(ConfigKey.KEY_GATEWAY_FLAG, False) + if gateway_flag: + gateway_host: str = self.config.get_value(ConfigKey.KEY_GATEWAY_HOST, "localhost") + gateway_port: int = self.config.get_value(ConfigKey.KEY_GATEWAY_PORT, 27941) + self.logger.info( + f"Start gateway launcher (host: {gateway_host}, port: {gateway_port})" + ) + + gateway_launcher = GatewayLauncher(gateway_host, gateway_port, self.logger) + + connector_thread_list: list[threading.Thread] = [] + for connector in self.connectors: + threads = connector.connect( + component_launcher, gateway_launcher, self.config, self.loader + ) + self.agent_thread_list.extend(threads) + + def connect() -> None: + for thread, event in threads.items(): + thread.daemon = True + thread.start() + event.wait(5) + + connector_thread = threading.Thread(target=connect) + connector_thread_list.append(connector_thread) + connector_thread.start() + + for thread in connector_thread_list: + thread.join() + + self.logger.info("All agents have been launched") + + for thread in self.agent_thread_list: + thread.join() + + def check_kernel_connection(self, host: str, port: int, timeout: int = 5) -> bool: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + result = sock.connect_ex((host, port)) + sock.close() + return result == 0 + except Exception as e: + self.logger.error(f"カーネルへの接続確認中にエラーが発生しました: {e}") + return False diff --git a/src/adf_core_python/core/launcher/config_key.py b/src/adf_core_python/core/launcher/config_key.py new file mode 100644 index 00000000..90d4f3c9 --- /dev/null +++ b/src/adf_core_python/core/launcher/config_key.py @@ -0,0 +1,31 @@ +from typing import Final + + +class ConfigKey: + # General + KEY_LOADER_CLASS: Final[str] = "adf_core_python.launcher.loader" + KEY_KERNEL_HOST: Final[str] = "kernel.host" + KEY_KERNEL_PORT: Final[str] = "kernel.port" + KEY_KERNEL_TIMEOUT: Final[str] = "kernel.timeout" + KEY_TEAM_NAME: Final[str] = "team.name" + KEY_DEBUG_FLAG: Final[str] = "adf.debug.flag" + KEY_DEVELOP_FLAG: Final[str] = "adf.develop.flag" + KEY_DEVELOP_DATA_FILE_NAME: Final[str] = "adf.develop.filename" + KEY_DEVELOP_DATA: Final[str] = "adf.develop.data" + KEY_MODULE_CONFIG_FILE_NAME: Final[str] = "adf.agent.moduleconfig.filename" + KEY_MODULE_DATA: Final[str] = "adf.agent.moduleconfig.data" + KEY_PRECOMPUTE: Final[str] = "adf.launcher.precompute" + # Platoon + KEY_AMBULANCE_TEAM_COUNT: Final[str] = "adf.team.platoon.ambulance.count" + KEY_FIRE_BRIGADE_COUNT: Final[str] = "adf.team.platoon.fire.count" + KEY_POLICE_FORCE_COUNT: Final[str] = "adf.team.platoon.police.count" + # Office + KEY_AMBULANCE_CENTRE_COUNT: Final[str] = "adf.team.office.ambulance.count" + KEY_FIRE_STATION_COUNT: Final[str] = "adf.team.office.fire.count" + KEY_POLICE_OFFICE_COUNT: Final[str] = "adf.team.office.police.count" + # adf-core-python + KEY_PRECOMPUTE_DATA_DIR: Final[str] = "adf.agent.precompute.dir_name" + # Gateway + KEY_GATEWAY_HOST: Final[str] = "gateway.host" + KEY_GATEWAY_PORT: Final[str] = "gateway.port" + KEY_GATEWAY_FLAG: Final[str] = "adf.gateway.flag" diff --git a/src/adf_core_python/core/launcher/connect/__init__.py b/src/adf_core_python/core/launcher/connect/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/launcher/connect/component_launcher.py b/src/adf_core_python/core/launcher/connect/component_launcher.py new file mode 100644 index 00000000..48903001 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/component_launcher.py @@ -0,0 +1,97 @@ +import socket +import time + +from structlog import BoundLogger + +from adf_core_python.core.agent.agent import Agent +from adf_core_python.core.launcher.connect.connection import Connection +from adf_core_python.core.launcher.connect.error.agent_error import AgentError +from adf_core_python.core.launcher.connect.error.server_error import ServerError + + +class ComponentLauncher: + def __init__(self, host: str, port: int, logger: BoundLogger) -> None: + self.request_id = 0 + self.port = port + self.host = host + self.logger = logger + + def make_connection(self) -> Connection: + return Connection(self.host, self.port) + + def connect(self, agent: Agent, _request_id: int) -> None: + connection = self.make_connection() + try: + connection.connect() + except socket.timeout: + self.logger.warning(f"Connection to {self.host}:{self.port} timed out") + return + except socket.error as e: + self.logger.exception( + f"Failed to connect to {self.host}:{self.port}", exception=str(e) + ) + return + + connection.message_received(agent.message_received) + agent.set_send_msg(connection.send_msg) + agent.start_up(_request_id) + + try: + connection.parse_message_from_kernel() + except AgentError as e: + self.logger.exception( + f"Agent error: {e}", + exception=str(e), + ) + except ServerError as e: + if isinstance(e.__cause__, EOFError): + self.logger.info(f"Connection closed by server (request_id={_request_id})") + else: + self.logger.exception("Server error", exception=str(e)) + + def generate_request_id(self) -> int: + self.request_id += 1 + return self.request_id + + def check_kernel_connection( + self, timeout: int = 30, retry_interval: float = 5.0 + ) -> bool: + """Attempts to connect to the kernel multiple times within the specified timeout period. + + Args: + timeout (int): Total timeout duration in seconds + retry_interval (float): Interval between retry attempts in seconds + + Returns: + bool: True if connection successful, False otherwise + """ + start_time = time.time() + attempt = 1 + + while True: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(retry_interval) + result = sock.connect_ex((self.host, self.port)) + sock.close() + + if result == 0: + self.logger.info(f"Successfully connected to kernel (attempt: {attempt})") + return True + + elapsed_time = time.time() - start_time + if elapsed_time >= timeout: + self.logger.error( + f"Timeout: Could not connect to kernel within {timeout} seconds (attempts: {attempt})" + ) + return False + + self.logger.debug( + f"Connection attempt {attempt} failed - retrying in {retry_interval} seconds" + ) + time.sleep(retry_interval) + attempt += 1 + + except Exception as e: + self.logger.error(f"Error while checking kernel connection: {e}") + return False diff --git a/src/adf_core_python/core/launcher/connect/connection.py b/src/adf_core_python/core/launcher/connect/connection.py new file mode 100644 index 00000000..3923471b --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connection.py @@ -0,0 +1,107 @@ +import socket +from typing import Any, Callable + +from rcrscore.proto import RCRSProto_pb2 + +from adf_core_python.core.launcher.connect.error.agent_error import AgentError +from adf_core_python.core.launcher.connect.error.server_error import ServerError + + +class Connection: + def __init__(self, host: str, port: int) -> None: + self.socket: socket.socket + self.agent = None + self.buffer_size: int = 4096 + self.data_buffer: bytes = b"" + self.host = host + self.port = port + + def connect(self) -> None: + """ + Connect to the kernel + + Raises + ------ + socket.timeout + If the connection times out + socket.error + If there is an error connecting to the socket + + """ + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect((self.host, self.port)) + + def parse_message_from_kernel(self) -> None: + """ + Parse messages from the kernel + + Raises + ------ + ServerError + If there is an error reading from the socket + AgentError + If there is an error in the agent calculation + """ + while True: + try: + msg = Connection._read_msg(self.socket) + except Exception as e: + raise ServerError(f"Error reading from socket: {e}") from e + try: + self.agent_message_received(msg) + except Exception as e: + raise AgentError(f"Error agent calculation: {e}") from e + + def message_received(self, agent_message_received: Callable) -> None: + self.agent_message_received = agent_message_received + + def send_msg(self, msg: Any) -> None: + Connection._write_msg(msg, self.socket) + + @staticmethod + def _write_int32(value: int, sock: socket.socket) -> None: + b = [ + ((value >> 24) & 0xFF), + ((value >> 16) & 0xFF), + ((value >> 8) & 0xFF), + (value & 0xFF), + ] + + sock.sendall(bytes(b)) + + @staticmethod + def _readnbytes(sock: socket.socket, n: int) -> bytes: + buff = b"" + while n > 0: + b = sock.recv(n) + buff += b + if len(b) == 0: + raise EOFError # peer socket has received a SH_WR shutdown + n -= len(b) + return buff + + @staticmethod + def _read_int32(sock: socket.socket) -> int: + byte_array = Connection._readnbytes(sock, 4) + value = int( + ((byte_array[0]) << 24) + + ((byte_array[1]) << 16) + + ((byte_array[2]) << 8) + + (byte_array[3]) + ) + return value + + @staticmethod + def _write_msg(msg: Any, sock: socket.socket) -> None: + out = msg.SerializeToString() + Connection._write_int32(len(out), sock) + + sock.sendall(out) + + @staticmethod + def _read_msg(sock: socket.socket) -> RCRSProto_pb2.MessageProto: + size = Connection._read_int32(sock) + content = Connection._readnbytes(sock, size) + message = RCRSProto_pb2.MessageProto() + message.ParseFromString(bytes(content)) + return message diff --git a/src/adf_core_python/core/launcher/connect/connector.py b/src/adf_core_python/core/launcher/connect/connector.py new file mode 100644 index 00000000..f58ce633 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector.py @@ -0,0 +1,26 @@ +import threading +from abc import ABC, abstractmethod +from typing import Optional + +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher + + +class Connector(ABC): + def __init__(self) -> None: + self.connected_agent_count = 0 + + @abstractmethod + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + raise NotImplementedError + + def get_connected_agent_count(self) -> int: + return self.connected_agent_count diff --git a/src/adf_core_python/core/launcher/connect/connector_ambulance_center.py b/src/adf_core_python/core/launcher/connect/connector_ambulance_center.py new file mode 100644 index 00000000..86e8fe88 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_ambulance_center.py @@ -0,0 +1,96 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office_ambulance import OfficeAmbulance +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_ambulance_center import ( + TacticsAmbulanceCenter, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorAmbulanceCenter(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_AMBULANCE_CENTRE_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_ambulance_center() is None: + self.logger.error("Cannot load ambulance centre tactics") + + tactics_ambulance_center: TacticsAmbulanceCenter = ( + loader.get_tactics_ambulance_center() + ) + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/ambulance_center" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + OfficeAmbulance( + tactics_ambulance_center, + "ambulance_center", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"AmbulanceCenterAgent-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/connector_ambulance_team.py b/src/adf_core_python/core/launcher/connect/connector_ambulance_team.py new file mode 100644 index 00000000..36456371 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_ambulance_team.py @@ -0,0 +1,94 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon_ambulance import PlatoonAmbulance +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_ambulance_team import ( + TacticsAmbulanceTeam, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorAmbulanceTeam(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_AMBULANCE_TEAM_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_ambulance_team() is None: + self.logger.error("Cannot load ambulance team tactics") + + tactics_ambulance_team: TacticsAmbulanceTeam = loader.get_tactics_ambulance_team() + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/ambulance_team" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + PlatoonAmbulance( + tactics_ambulance_team, + "ambulance_team", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"AmbulanceTeam-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/connector_fire_brigade.py b/src/adf_core_python/core/launcher/connect/connector_fire_brigade.py new file mode 100644 index 00000000..8889365a --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_fire_brigade.py @@ -0,0 +1,94 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon_fire import PlatoonFire +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_fire_brigade import ( + TacticsFireBrigade, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorFireBrigade(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_FIRE_BRIGADE_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_fire_brigade() is None: + self.logger.error("Cannot load fire brigade tactics") + + tactics_fire_brigade: TacticsFireBrigade = loader.get_tactics_fire_brigade() + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/fire_brigade" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + PlatoonFire( + tactics_fire_brigade, + "fire_brigade", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"FireBrigadeAgent-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/connector_fire_station.py b/src/adf_core_python/core/launcher/connect/connector_fire_station.py new file mode 100644 index 00000000..d1bdf7a4 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_fire_station.py @@ -0,0 +1,94 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office_fire import OfficeFire +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_fire_station import ( + TacticsFireStation, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorFireStation(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_FIRE_STATION_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_fire_station() is None: + self.logger.error("Cannot load fire station tactics") + + tactics_fire_station: TacticsFireStation = loader.get_tactics_fire_station() + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/fire_station" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + OfficeFire( + tactics_fire_station, + "fire_station", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"FireStationAgent-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/connector_police_force.py b/src/adf_core_python/core/launcher/connect/connector_police_force.py new file mode 100644 index 00000000..45acf864 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_police_force.py @@ -0,0 +1,94 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.platoon.platoon_police import PlatoonPolice +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_police_force import ( + TacticsPoliceForce, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorPoliceForce(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_POLICE_FORCE_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_police_force() is None: + self.logger.error("Cannot load police force tactics") + + tactics_police_force: TacticsPoliceForce = loader.get_tactics_police_force() + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/police_force" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + PlatoonPolice( + tactics_police_force, + "police_force", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"PoliceForceAgent-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/connector_police_office.py b/src/adf_core_python/core/launcher/connect/connector_police_office.py new file mode 100644 index 00000000..97d75f97 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/connector_police_office.py @@ -0,0 +1,94 @@ +import threading +from typing import Optional + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.office.office_police import OfficePolice +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_police_office import ( + TacticsPoliceOffice, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.gateway.gateway_agent import GatewayAgent +from adf_core_python.core.gateway.gateway_launcher import GatewayLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.launcher.connect.component_launcher import ComponentLauncher +from adf_core_python.core.launcher.connect.connector import Connector +from adf_core_python.core.logger.logger import get_logger + + +class ConnectorPoliceOffice(Connector): + def __init__(self) -> None: + super().__init__() + self.logger = get_logger(__name__) + + def connect( + self, + component_launcher: ComponentLauncher, + gateway_launcher: Optional[GatewayLauncher], + config: Config, + loader: AbstractLoader, + ) -> dict[threading.Thread, threading.Event]: + count: int = config.get_value(ConfigKey.KEY_POLICE_OFFICE_COUNT, 0) + if count == 0: + return {} + + threads: dict[threading.Thread, threading.Event] = {} + + for _ in range(count): + if loader.get_tactics_police_office() is None: + self.logger.error("Cannot load police office tactics") + + tactics_police_office: TacticsPoliceOffice = loader.get_tactics_police_office() + + module_config: ModuleConfig = ModuleConfig( + config.get_value( + ConfigKey.KEY_MODULE_CONFIG_FILE_NAME, + ModuleConfig.DEFAULT_CONFIG_FILE_NAME, + ) + ) + + develop_data: DevelopData = DevelopData( + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + config.get_value( + ConfigKey.KEY_DEVELOP_DATA_FILE_NAME, DevelopData.DEFAULT_FILE_NAME + ), + ) + + precompute_data_dir: str = f"{config.get_value(ConfigKey.KEY_PRECOMPUTE_DATA_DIR, 'precompute')}/police_office" + + finish_post_connect_event = threading.Event() + request_id: int = component_launcher.generate_request_id() + + gateway_agent: Optional[GatewayAgent] = None + if isinstance(gateway_launcher, GatewayLauncher): + gateway_agent = GatewayAgent(gateway_launcher) + if isinstance(gateway_agent, GatewayAgent): + gateway_thread = threading.Thread( + target=gateway_launcher.connect, + args=(gateway_agent,), + ) + gateway_thread.daemon = True + gateway_thread.start() + + component_thread = threading.Thread( + target=component_launcher.connect, + args=( + OfficePolice( + tactics_police_office, + "police_office", + config.get_value(ConfigKey.KEY_PRECOMPUTE, False), + config.get_value(ConfigKey.KEY_DEBUG_FLAG, False), + precompute_data_dir, + module_config, + develop_data, + finish_post_connect_event, + gateway_agent, + ), + request_id, + ), + name=f"PoliceOfficeAgent-{request_id}", + ) + threads[component_thread] = finish_post_connect_event + + return threads diff --git a/src/adf_core_python/core/launcher/connect/error/agent_error.py b/src/adf_core_python/core/launcher/connect/error/agent_error.py new file mode 100644 index 00000000..7eee9fe9 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/error/agent_error.py @@ -0,0 +1,2 @@ +class AgentError(Exception): + pass diff --git a/src/adf_core_python/core/launcher/connect/error/server_error.py b/src/adf_core_python/core/launcher/connect/error/server_error.py new file mode 100644 index 00000000..a1d0ea29 --- /dev/null +++ b/src/adf_core_python/core/launcher/connect/error/server_error.py @@ -0,0 +1,2 @@ +class ServerError(Exception): + pass diff --git a/src/adf_core_python/core/logger/__init__.py b/src/adf_core_python/core/logger/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/core/logger/logger.py b/src/adf_core_python/core/logger/logger.py new file mode 100644 index 00000000..1f9a61dc --- /dev/null +++ b/src/adf_core_python/core/logger/logger.py @@ -0,0 +1,96 @@ +import logging +import os +import sys +from datetime import datetime + +import structlog +from structlog.dev import ConsoleRenderer +from structlog.processors import JSONRenderer + +from adf_core_python.core.agent.info.agent_info import AgentInfo + + +def get_logger(name: str) -> structlog.BoundLogger: + """ + Get a logger with the given name. + For kernel logging, use this function to get a logger. + + Parameters + ---------- + name : str + The name of the logger. + + Returns + ------- + structlog.BoundLogger + The logger with the given name. + """ + return structlog.get_logger(name) + + +def get_agent_logger(name: str, agent_info: AgentInfo) -> structlog.BoundLogger: + """ + Get a logger with the given name and agent information. + For agent logging, use this function to get a logger. + + Parameters + ---------- + name : str + The name of the logger. + agent_info : AgentInfo + The agent information. + + Returns + ------- + structlog.BoundLogger + The logger with the given name and agent information. + """ + agent = agent_info.get_myself() + if agent is None: + raise ValueError("Agent information is not available") + + return structlog.get_logger(name).bind( + agent_id=str(agent_info.get_entity_id()), + agent_type=str(agent.get_urn().name), + ) + + +def configure_logger() -> None: + # 既存のログファイルが存在する場合、日付付きでバックアップする + log_file = "agent.log" + if os.path.exists(log_file): + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_file = f"agent_{timestamp}.log" + os.rename(log_file, backup_file) + + structlog.configure( + processors=[ + structlog.stdlib.add_log_level, + structlog.stdlib.add_logger_name, + structlog.stdlib.PositionalArgumentsFormatter(), + structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M.%S", utc=False), + structlog.processors.StackInfoRenderer(), + structlog.processors.UnicodeDecoder(), + structlog.stdlib.ProcessorFormatter.wrap_for_formatter, + ], + logger_factory=structlog.stdlib.LoggerFactory(), + wrapper_class=structlog.stdlib.BoundLogger, + cache_logger_on_first_use=True, + ) + + handler_stdout = logging.StreamHandler(sys.stdout) + handler_stdout.setFormatter( + structlog.stdlib.ProcessorFormatter(processor=ConsoleRenderer()) + ) + handler_stdout.setLevel(logging.INFO) + + handler_file = logging.FileHandler(log_file) + handler_file.setFormatter( + structlog.stdlib.ProcessorFormatter(processor=JSONRenderer()) + ) + handler_file.setLevel(logging.DEBUG) + + root_logger = logging.getLogger() + root_logger.addHandler(handler_stdout) + root_logger.addHandler(handler_file) + root_logger.setLevel(logging.DEBUG) diff --git a/src/adf_core_python/implement/__init__.py b/src/adf_core_python/implement/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/action/__init__.py b/src/adf_core_python/implement/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/action/default_extend_action_clear.py b/src/adf_core_python/implement/action/default_extend_action_clear.py new file mode 100644 index 00000000..ad837e9b --- /dev/null +++ b/src/adf_core_python/implement/action/default_extend_action_clear.py @@ -0,0 +1,752 @@ +import math +import sys +from typing import Optional, cast + +from rcrscore.entities import ( + AmbulanceTeam, + Area, + Blockade, + Building, + EntityID, + FireBrigade, + Human, + PoliceForce, + Refuge, + Road, +) +from shapely import LineString, Point, Polygon + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.action.police.action_clear import ActionClear +from adf_core_python.core.agent.action.police.action_clear_area import ActionClearArea +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultExtendActionClear(ExtendAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._clear_distance = float( + self.scenario_info.get_value("clear.repair.distance", 0.0) + ) + self._forced_move = float( + develop_data.get_value( + "adf_core_python.implement.action.DefaultExtendActionClear.forced_move", + 3, + ) + ) + self._threshold_rest = float( + develop_data.get_value( + "adf_core_python.implement.action.DefaultExtendActionClear.rest", 100 + ) + ) + + self._target_entity_id: Optional[EntityID] = None + self._move_point_cache: dict[EntityID, Optional[set[tuple[float, float]]]] = {} + self._old_clear_x = 0 + self._old_clear_y = 0 + self.count = 0 + + self._path_planning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionClear.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtendAction: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + return self + + def prepare(self) -> ExtendAction: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + return self + + def update_info(self, message_manager: MessageManager) -> ExtendAction: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtendAction: + self._target_entity_id = None + target_entity = self.world_info.get_entity(target_entity_id) + if target_entity is not None: + if isinstance(target_entity, Road): + self._target_entity_id = target_entity_id + elif isinstance(target_entity, Blockade): + self._target_entity_id = target_entity.get_position() + elif isinstance(target_entity, Building): + self._target_entity_id = target_entity_id + return self + + def calculate(self) -> ExtendAction: + self.result = None + police_force = cast(PoliceForce, self.agent_info.get_myself()) + + if self._need_rest(police_force): + target_entity_ids: list[EntityID] = [] + if self._target_entity_id is not None: + target_entity_ids.append(self._target_entity_id) + + self.result = self._calc_rest( + police_force, self._path_planning, target_entity_ids + ) + if self.result is not None: + return self + + if self._target_entity_id is None: + return self + + agent_position_entity_id = police_force.get_position() + if agent_position_entity_id is None: + return self + target_entity = self.world_info.get_entity(self._target_entity_id) + position_entity = self.world_info.get_entity(agent_position_entity_id) + if target_entity is None or isinstance(target_entity, Area) is False: + return self + if isinstance(position_entity, Road): + self.result = self._get_rescue_action(police_force, position_entity) + if self.result is not None: + return self + + if agent_position_entity_id == self._target_entity_id: + self.result = self._get_area_clear_action( + police_force, cast(Road, position_entity) + ) + if self.result is not None: + return self + elif cast(Area, target_entity).get_edge_to(agent_position_entity_id) is not None: + self.result = self._get_neighbour_position_action( + police_force, cast(Area, target_entity) + ) + else: + path = self._path_planning.get_path( + agent_position_entity_id, self._target_entity_id + ) + if path is not None and len(path) > 0: + index = self._index_of(path, agent_position_entity_id) + if index == -1: + area = cast(Area, position_entity) + for i in range(0, len(path), 1): + if area.get_edge_to(path[i]) is not None: + index = i + break + + elif index >= 0: + index += 1 + + if index >= 0 and index < len(path): + entity = self.world_info.get_entity(path[index]) + self.result = self._get_neighbour_position_action( + police_force, cast(Area, entity) + ) + if self.result is not None and isinstance(self.result, ActionMove): + action_move = self.result + if action_move.is_destination_defined(): + self.result = None + + if self.result is None: + self.result = ActionMove(path) + + return self + + def _need_rest(self, police_force: PoliceForce) -> bool: + hp = police_force.get_hp() + damage = police_force.get_damage() + + if hp is None or damage is None or hp == 0 or damage == 0: + return False + + active_time = (hp / damage) + (1 if (hp % damage) != 0 else 0) + if self._kernel_time == -1: + self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + + return damage is not None and ( + damage >= self._threshold_rest + or (active_time + self.agent_info.get_time() < self._kernel_time) + ) + + def _calc_rest( + self, + police_force: PoliceForce, + path_planning: PathPlanning, + target_entity_ids: list[EntityID], + ) -> Optional[Action]: + position_entity_id = police_force.get_position() + if position_entity_id is None: + return None + refuges = self.world_info.get_entity_ids_of_types([Refuge]) + current_size = len(refuges) + if position_entity_id in refuges: + return ActionRest() + + first_result: list[EntityID] = [] + while len(refuges) > 0: + path = path_planning.get_path(position_entity_id, refuges[0]) + if path is not None and len(path) > 0: + if first_result == []: + first_result = path.copy() + if target_entity_ids == []: + break + + refuge_entity_id = path[-1] + from_refuge_to_target_path = path_planning.get_path( + refuge_entity_id, target_entity_ids[0] + ) + if from_refuge_to_target_path != []: + return ActionMove(path) + + refuges.remove(refuge_entity_id) + if current_size == len(refuges): + break + current_size = len(refuges) + else: + break + + return ActionMove(first_result) if first_result != [] else None + + def _get_rescue_action( + self, police_entity: PoliceForce, road: Road + ) -> Optional[Action]: + road_blockades = road.get_blockades() + blockades = set( + [] + if road_blockades is None + else [ + cast(Blockade, self.world_info.get_entity(blockade_entity_id)) + for blockade_entity_id in road_blockades + ] + ) + agent_entities = set( + self.world_info.get_entities_of_types([AmbulanceTeam, FireBrigade]) + ) + + police_x = police_entity.get_x() + police_y = police_entity.get_y() + min_distance = sys.float_info.max + move_action: Optional[ActionMove] = None + + for agent_entity in agent_entities: + human = cast(Human, agent_entity) + human_position = human.get_position() + if ( + human_position is None + or human_position.get_value() != road.get_entity_id().get_value() + ): + continue + + human_x = human.get_x() + human_y = human.get_y() + if human_x is None or human_y is None or police_x is None or police_y is None: + continue + + action_clear: Optional[ActionClear | ActionClearArea] = None + clear_blockade: Optional[Blockade] = None + for blockade in blockades: + blockade_apexes = blockade.get_apexes() + if blockade_apexes is None or not self._is_inside( + human_x, human_y, blockade_apexes + ): + continue + + distance = self._get_distance(police_x, police_y, human_x, human_y) + if self._is_intersecting_area(police_x, police_y, human_x, human_y, road): + action = self._get_intersect_edge_action( + police_x, police_y, human_x, human_y, road + ) + if action is None: + continue + if isinstance(action, ActionClear): + if action_clear is None: + action_clear = action + clear_blockade = blockade + continue + + if clear_blockade is not None: + if self._is_intersecting_blockades(blockade, clear_blockade): + return ActionClear(clear_blockade) + + another_distance = self.world_info.get_distance( + police_entity.get_entity_id(), + clear_blockade.get_entity_id(), + ) + blockade_distance = self.world_info.get_distance( + police_entity.get_entity_id(), blockade.get_entity_id() + ) + if blockade_distance < another_distance: + return action + + return action_clear + elif isinstance(action, ActionMove) and distance < min_distance: + min_distance = distance + move_action = action + + elif self._is_intersecting_blockade( + police_x, police_y, human_x, human_y, blockade + ): + vector = self._scale_clear( + self._get_vector(police_x, police_y, human_x, human_y) + ) + clear_x = int(police_x + vector[0]) + clear_y = int(police_y + vector[1]) + + vector = self._scale_back_clear(vector) + start_x = int(police_x + vector[0]) + start_y = int(police_y + vector[1]) + + if self._is_intersecting_blockade( + start_x, start_y, clear_x, clear_y, blockade + ): + if action_clear is None: + action_clear = ActionClearArea(clear_x, clear_y) + clear_blockade = blockade + else: + if clear_blockade is not None: + if self._is_intersecting_blockades(blockade, clear_blockade): + return ActionClear(clear_blockade) + + distance1 = self.world_info.get_distance( + police_entity.get_entity_id(), + clear_blockade.get_entity_id(), + ) + distance2 = self.world_info.get_distance( + police_entity.get_entity_id(), + blockade.get_entity_id(), + ) + if distance1 > distance2: + return ActionClearArea(clear_x, clear_y) + + return action_clear + + elif distance < min_distance: + min_distance = distance + move_action = ActionMove([road.get_entity_id()], human_x, human_y) + + if action_clear is not None: + return action_clear + + return move_action + + def _is_inside(self, x: float, y: float, apexes: list[int]) -> bool: + point = Point(x, y) + polygon = Polygon([(apexes[i], apexes[i + 1]) for i in range(0, len(apexes), 2)]) + return polygon.contains(point) + + def _get_distance(self, x1: float, y1: float, x2: float, y2: float) -> float: + return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 + + def _is_intersecting_area( + self, agent_x: float, agent_y: float, point_x: float, point_y: float, area: Area + ) -> bool: + edges = area.get_edges() + if edges is None: + return False + for edge in edges: + start_x = edge.get_start_x() + start_y = edge.get_start_y() + end_x = edge.get_end_x() + end_y = edge.get_end_y() + + line1 = LineString([(agent_x, agent_y), (point_x, point_y)]) + line2 = LineString([(start_x, start_y), (end_x, end_y)]) + if line1.intersects(line2): + mid_x = (start_x + end_x) / 2.0 + mid_y = (start_y + end_y) / 2.0 + if not self._equals_point( + agent_x, agent_y, mid_x, mid_y, 1000 + ) and not self._equals_point(point_x, point_y, mid_x, mid_y, 1000): + return True + + return False + + def _equals_point( + self, x1: float, y1: float, x2: float, y2: float, range: float + ) -> bool: + return (x2 - range < x1 and x1 < x2 + range) and ( + y2 - range < y1 and y1 < y2 + range + ) + + def _get_intersect_edge_action( + self, agent_x: float, agent_y: float, point_x: float, point_y: float, road: Road + ) -> Action: + move_points = self._get_move_points(road) + best_point: Optional[tuple[float, float]] = None + best_distance = sys.float_info.max + for point in move_points: + if not self._is_intersecting_area(agent_x, agent_y, point[0], point[1], road): + if not self._is_intersecting_area(point_x, point_y, point[0], point[1], road): + distance = self._get_distance(point_x, point_y, point[0], point[1]) + if distance < best_distance: + best_point = point + best_distance = distance + + if best_point is not None: + bp_x, bp_y = best_point + if road.get_blockades() is None: + return ActionMove([road.get_entity_id()], int(bp_x), int(bp_y)) + + action_clear: Optional[ActionClearArea] = None + clear_blockade: Optional[Blockade] = None + action_move: Optional[ActionMove] = None + + vector = self._scale_clear(self._get_vector(agent_x, agent_y, bp_x, bp_y)) + clear_x = int(agent_x + vector[0]) + clear_y = int(agent_x + vector[1]) + + vector = self._scale_back_clear(vector) + start_x = int(agent_x + vector[0]) + start_y = int(agent_y + vector[1]) + + for blockade in self.world_info.get_blockades(road): + if self._is_intersecting_blockade(start_x, start_y, bp_x, bp_y, blockade): + if self._is_intersecting_blockade( + start_x, start_y, clear_x, clear_y, blockade + ): + if action_clear is None: + action_clear = ActionClearArea(clear_x, clear_y) + clear_blockade = blockade + else: + if clear_blockade is not None and self._is_intersecting_blockades( + blockade, clear_blockade + ): + return ActionClear(clear_blockade) + return action_clear + elif action_move is None: + action_move = ActionMove([road.get_entity_id()], int(bp_x), int(bp_y)) + + if action_clear is not None: + return action_clear + if action_move is not None: + return action_move + + action = self._get_area_clear_action( + cast(PoliceForce, self.agent_info.get_myself()), road + ) + if action is None: + action = ActionMove([road.get_entity_id()], int(point_x), int(point_y)) + return action + + def _get_move_points(self, road: Road) -> set[tuple[float, float]]: + points: Optional[set[tuple[float, float]]] = self._move_point_cache.get( + road.get_entity_id() + ) + if points is None: + points = set() + apex = road.get_apexes() + for i in range(0, len(apex), 2): + for j in range(i + 2, len(apex), 2): + mid_x = (apex[i] + apex[j]) / 2.0 + mid_y = (apex[i + 1] + apex[j + 1]) / 2.0 + if self._is_inside(mid_x, mid_y, apex): + points.add((mid_x, mid_y)) + + edges = road.get_edges() + if edges is not None: + for edge in edges: + mid_x = (edge.get_start_x() + edge.get_end_x()) / 2.0 + mid_y = (edge.get_start_y() + edge.get_end_y()) / 2.0 + if (mid_x, mid_y) in points: + points.remove((mid_x, mid_y)) + + self._move_point_cache[road.get_entity_id()] = points + + return points + + def _get_vector( + self, from_x: float, from_y: float, to_x: float, to_y: float + ) -> tuple[float, float]: + return (to_x - from_x, to_y - from_y) + + def _scale_clear(self, vector: tuple[float, float]) -> tuple[float, float]: + length = 1.0 / math.hypot(vector[0], vector[1]) + return ( + vector[0] * length * self._clear_distance, + vector[1] * length * self._clear_distance, + ) + + def _scale_back_clear(self, vector: tuple[float, float]) -> tuple[float, float]: + length = 1.0 / math.hypot(vector[0], vector[1]) + return (vector[0] * length * -510, vector[1] * length * -510) + + def _is_intersecting_blockade( + self, + agent_x: float, + agent_y: float, + point_x: float, + point_y: float, + blockade: Blockade, + ) -> bool: + apexes = blockade.get_apexes() + if apexes is None or len(apexes) < 4: + return False + for i in range(0, len(apexes) - 3, 2): + line1 = LineString([(apexes[i], apexes[i + 1]), (apexes[i + 2], apexes[i + 3])]) + line2 = LineString([(agent_x, agent_y), (point_x, point_y)]) + if line1.intersects(line2): + return True + return False + + def _is_intersecting_blockades( + self, blockade1: Blockade, blockade2: Blockade + ) -> bool: + apexes1 = blockade1.get_apexes() + apexes2 = blockade2.get_apexes() + if apexes1 is None or apexes2 is None or len(apexes1) < 4 or len(apexes2) < 4: + return False + for i in range(0, len(apexes1) - 2, 2): + for j in range(0, len(apexes2) - 2, 2): + line1 = LineString( + [(apexes1[i], apexes1[i + 1]), (apexes1[i + 2], apexes1[i + 3])] + ) + line2 = LineString( + [(apexes2[j], apexes2[j + 1]), (apexes2[j + 2], apexes2[j + 3])] + ) + if line1.intersects(line2): + return True + + for i in range(0, len(apexes1) - 2, 2): + line1 = LineString( + [(apexes1[i], apexes1[i + 1]), (apexes1[i + 2], apexes1[i + 3])] + ) + line2 = LineString([(apexes2[-2], apexes2[-1]), (apexes2[0], apexes2[1])]) + if line1.intersects(line2): + return True + + for i in range(0, len(apexes2) - 2, 2): + line1 = LineString([(apexes1[-2], apexes1[-1]), (apexes1[0], apexes1[1])]) + line2 = LineString( + [(apexes2[i], apexes2[i + 1]), (apexes2[i + 2], apexes2[i + 3])] + ) + if line1.intersects(line2): + return True + + return False + + def _get_area_clear_action( + self, police_entity: PoliceForce, road: Road + ) -> Optional[Action]: + if road.get_blockades() == []: + return None + + blockades = set(self.world_info.get_blockades(road)) + min_distance = sys.float_info.max + clear_blockade: Optional[Blockade] = None + for blockade in blockades: + for another in blockades: + if blockade == another: + continue + + if self._is_intersecting_blockades(blockade, another): + distance1 = self.world_info.get_distance( + police_entity.get_entity_id(), blockade.get_entity_id() + ) + distance2 = self.world_info.get_distance( + police_entity.get_entity_id(), another.get_entity_id() + ) + if distance1 <= distance2 and distance1 < min_distance: + min_distance = distance1 + clear_blockade = blockade + elif distance2 < min_distance: + min_distance = distance2 + clear_blockade = another + + if clear_blockade is not None: + if min_distance < self._clear_distance: + return ActionClear(clear_blockade) + else: + position = police_entity.get_position() + if position is not None: + return ActionMove( + [position], + clear_blockade.get_x(), + clear_blockade.get_y(), + ) + + agent_x = police_entity.get_x() + agent_y = police_entity.get_y() + if agent_x is None or agent_y is None: + return None + clear_blockade = None + min_point_distance = sys.float_info.max + clear_x = 0 + clear_y = 0 + for blockade in blockades: + apexes = blockade.get_apexes() + if apexes is None or len(apexes) < 4: + continue + for i in range(0, len(apexes) - 2, 2): + distance = self._get_distance(agent_x, agent_y, apexes[i], apexes[i + 1]) + if distance < min_point_distance: + clear_blockade = blockade + min_point_distance = distance + clear_x = apexes[i] + clear_y = apexes[i + 1] + + if clear_blockade is not None: + if min_point_distance < self._clear_distance: + vector = self._scale_clear(self._get_vector(agent_x, agent_y, clear_x, clear_y)) + clear_x = int(agent_x + vector[0]) + clear_y = int(agent_y + vector[1]) + return ActionClearArea(clear_x, clear_y) + position = police_entity.get_position() + if position is not None: + return ActionMove([position], clear_x, clear_y) + + return None + + def _index_of(self, list: list[EntityID], x: EntityID) -> int: + return list.index(x) if x in list else -1 + + def _get_neighbour_position_action( + self, police_entity: PoliceForce, target: Area + ) -> Optional[Action]: + agent_x = police_entity.get_x() + agent_y = police_entity.get_y() + if agent_x is None or agent_y is None: + return None + position_id = police_entity.get_position() + if position_id is None: + return None + position = self.world_info.get_entity(position_id) + if position is None: + return None + + edge = target.get_edge_to(position.get_entity_id()) + if edge is None: + return None + + if isinstance(position, Road): + road = position + if road.get_blockades() != []: + mid_x = (edge.get_start_x() + edge.get_end_x()) / 2.0 + mid_y = (edge.get_start_y() + edge.get_end_y()) / 2.0 + if self._is_intersecting_area(agent_x, agent_y, mid_x, mid_y, road): + return self._get_intersect_edge_action(agent_x, agent_y, mid_x, mid_y, road) + + action_clear: Optional[ActionClear | ActionClearArea] = None + clear_blockade: Optional[Blockade] = None + action_move: Optional[ActionMove] = None + + vector = self._scale_clear(self._get_vector(agent_x, agent_y, mid_x, mid_y)) + clear_x = int(agent_x + vector[0]) + clear_y = int(agent_y + vector[1]) + + vector = self._scale_back_clear(vector) + start_x = int(agent_x + vector[0]) + start_y = int(agent_y + vector[1]) + + for blockade in self.world_info.get_blockades(road): + if self._is_intersecting_blockade(start_x, start_y, mid_x, mid_y, blockade): + if self._is_intersecting_blockade( + start_x, start_y, clear_x, clear_y, blockade + ): + if action_clear is None: + action_clear = ActionClearArea(clear_x, clear_y) + clear_blockade = blockade + if self._equals_point( + self._old_clear_x, + self._old_clear_y, + clear_x, + clear_y, + 1000, + ): + if self.count >= self._forced_move: + self.count = 0 + return ActionMove( + [road.get_entity_id()], + int(clear_x), + int(clear_y), + ) + self.count += 1 + + self._old_clear_x = clear_x + self._old_clear_y = clear_y + else: + if clear_blockade is not None: + if self._is_intersecting_blockades(blockade, clear_blockade): + return ActionClear(clear_blockade) + + return action_clear + elif action_move is None: + action_move = ActionMove([road.get_entity_id()], int(mid_x), int(mid_y)) + + if action_clear is not None: + return action_clear + if action_move is not None: + return action_move + + if isinstance(target, Road): + road = target + if road.get_blockades() == []: + return ActionMove([position.get_entity_id(), target.get_entity_id()]) + + target_blockade: Optional[Blockade] = None + min_point_distance = sys.float_info.max + clear_x = 0 + clear_y = 0 + for blockade in self.world_info.get_blockades(road): + apexes = blockade.get_apexes() + if apexes is None or len(apexes) < 4: + continue + for i in range(0, len(apexes) - 2, 2): + distance = self._get_distance(agent_x, agent_y, apexes[i], apexes[i + 1]) + if distance < min_point_distance: + target_blockade = blockade + min_point_distance = distance + clear_x = apexes[i] + clear_y = apexes[i + 1] + + if target_blockade is not None and min_point_distance < self._clear_distance: + vector = self._scale_clear(self._get_vector(agent_x, agent_y, clear_x, clear_y)) + clear_x = int(agent_x + vector[0]) + clear_y = int(agent_y + vector[1]) + if self._equals_point( + self._old_clear_x, self._old_clear_y, clear_x, clear_y, 1000 + ): + if self.count >= self._forced_move: + self.count = 0 + return ActionMove([road.get_entity_id()], clear_x, clear_y) + self.count += 1 + + self._old_clear_x = clear_x + self._old_clear_y = clear_y + return ActionClearArea(clear_x, clear_y) + + return ActionMove([position.get_entity_id(), target.get_entity_id()]) diff --git a/src/adf_core_python/implement/action/default_extend_action_move.py b/src/adf_core_python/implement/action/default_extend_action_move.py new file mode 100644 index 00000000..7287535a --- /dev/null +++ b/src/adf_core_python/implement/action/default_extend_action_move.py @@ -0,0 +1,104 @@ +from typing import Optional, cast + +from rcrscore.entities import Area, Blockade, Entity, EntityID, Human + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultExtendActionMove(ExtendAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._target_entity_id: Optional[EntityID] = None + self._threshold_to_rest: int = develop_data.get_value("threshold_to_rest", 100) + + self._path_planning: PathPlanning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionMove.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: + super().precompute(precompute_data) + if self.get_count_precompute() > 1: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtendAction: + super().resume(precompute_data) + if self.get_count_resume() > 1: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> ExtendAction: + super().prepare() + if self.get_count_prepare() > 1: + return self + self._path_planning.prepare() + return self + + def update_info(self, message_manager: MessageManager) -> ExtendAction: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtendAction: + entity: Optional[Entity] = self.world_info.get_entity(target_entity_id) + self._target_entity_id = None + + if entity is None: + return self + + if isinstance(entity, Blockade) or isinstance(entity, Human): + position: Optional[EntityID] = entity.get_position() + if position is None: + return self + entity = self.world_info.get_entity(position) + + if entity is not None and isinstance(entity, Area): + self._target_entity_id = entity.get_entity_id() + + return self + + def calculate(self) -> ExtendAction: + self.result = None + agent: Human = cast(Human, self.agent_info.get_myself()) + + if self._target_entity_id is None: + return self + + agent_position = agent.get_position() + if agent_position is None: + return self + + path: list[EntityID] = self._path_planning.get_path( + agent_position, self._target_entity_id + ) + + if path is not None and len(path) != 0: + self.result = ActionMove(path) + + return self diff --git a/src/adf_core_python/implement/action/default_extend_action_rescue.py b/src/adf_core_python/implement/action/default_extend_action_rescue.py new file mode 100644 index 00000000..489f5d58 --- /dev/null +++ b/src/adf_core_python/implement/action/default_extend_action_rescue.py @@ -0,0 +1,155 @@ +from typing import Optional, cast + +from rcrscore.entities import Area, Blockade, EntityID, FireBrigade, Human + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.action.ambulance.action_rescue import ActionRescue +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ( + ScenarioInfo, + ScenarioInfoKeys, +) +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultExtendActionRescue(ExtendAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._kernel_time: int = -1 + self._target_entity_id: Optional[EntityID] = None + self._threshold_rest = develop_data.get_value( + "adf_core_python.implement.action.DefaultExtendActionRescue.rest", 100 + ) + + self._path_planning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionRescue.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtendAction: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) + return self + + def prepare(self) -> ExtendAction: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) + return self + + def update_info(self, message_manager: MessageManager) -> ExtendAction: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtendAction: + self._target_entity_id = None + if target_entity_id is not None: + entity = self.world_info.get_entity(target_entity_id) + if isinstance(entity, Human) or isinstance(entity, Area): + self._target_entity_id = target_entity_id + return self + return self + + def calculate(self) -> ExtendAction: + self.result = None + agent = cast(FireBrigade, self.agent_info.get_myself()) + + if self._target_entity_id is not None: + self.result = self._calc_rescue( + agent, self._path_planning, self._target_entity_id + ) + + return self + + def _calc_rescue( + self, + agent: FireBrigade, + path_planning: PathPlanning, + target_entity_id: EntityID, + ) -> Optional[Action]: + target_entity = self.world_info.get_entity(target_entity_id) + if target_entity is None: + return None + + agent_position_entity_id = agent.get_position() + if agent_position_entity_id is None: + return None + + if isinstance(target_entity, Human): + human = target_entity + if human.get_hp() == 0: + return None + + target_position_entity_id = human.get_position() + if target_position_entity_id is None: + return None + + if agent_position_entity_id == target_position_entity_id: + buriedness = human.get_buriedness() + if buriedness is not None and buriedness > 0: + return ActionRescue(target_entity_id) + else: + path = path_planning.get_path( + agent_position_entity_id, target_position_entity_id + ) + if path != []: + return ActionMove(path) + + return None + + if isinstance(target_entity, Blockade): + blockade = target_entity + blockade_position = blockade.get_position() + if blockade_position is None: + return None + + target_entity = self.world_info.get_entity(blockade_position) + if isinstance(target_entity, Area): + path = self._path_planning.get_path( + agent_position_entity_id, target_entity.get_entity_id() + ) + if path != []: + return ActionMove(path) + + return None diff --git a/src/adf_core_python/implement/action/default_extend_action_transport.py b/src/adf_core_python/implement/action/default_extend_action_transport.py new file mode 100644 index 00000000..b6476685 --- /dev/null +++ b/src/adf_core_python/implement/action/default_extend_action_transport.py @@ -0,0 +1,254 @@ +from typing import Optional, Union, cast + +from rcrscore.entities import ( + AmbulanceTeam, + Area, + Civilian, + Entity, + EntityID, + Human, + Refuge, +) + +from adf_core_python.core.agent.action.ambulance.action_load import ActionLoad +from adf_core_python.core.agent.action.ambulance.action_unload import ActionUnload +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +from adf_core_python.core.logger.logger import get_agent_logger + + +class DefaultExtendActionTransport(ExtendAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._target_entity_id: Optional[EntityID] = None + self._threshold_to_rest: int = develop_data.get_value("threshold_to_rest", 100) + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self.agent_info, + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionMove.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: + super().precompute(precompute_data) + if self.get_count_precompute() > 1: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtendAction: + super().resume(precompute_data) + if self.get_count_resume() > 1: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> ExtendAction: + super().prepare() + if self.get_count_prepare() > 1: + return self + self._path_planning.prepare() + return self + + def update_info(self, message_manager: MessageManager) -> ExtendAction: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtendAction: + entity: Optional[Entity] = self.world_info.get_world_model().get_entity( + target_entity_id + ) + self._target_entity_id = None + + if entity is None: + return self + + if isinstance(entity, Human) or isinstance(entity, Area): + self._target_entity_id = target_entity_id + + return self + + def calculate(self) -> ExtendAction: + self._result = None + agent: AmbulanceTeam = cast(AmbulanceTeam, self.agent_info.get_myself()) + transport_human: Optional[Human] = self.agent_info.some_one_on_board() + if transport_human is not None: + self._logger.debug(f"transport_human: {transport_human.get_entity_id()}") + self.result = self.calc_unload( + agent, self._path_planning, transport_human, self._target_entity_id + ) + if self.result is not None: + return self + + if self._target_entity_id is not None: + self.result = self.calc_rescue(agent, self._path_planning, self._target_entity_id) + + return self + + def calc_rescue( + self, + agent: AmbulanceTeam, + path_planning: PathPlanning, + target_id: EntityID, + ) -> Optional[Union[ActionMove, ActionLoad]]: + target_entity = self.world_info.get_entity(target_id) + if target_entity is None: + return None + + agent_position = agent.get_position() + if agent_position is None: + return None + if isinstance(target_entity, Human): + human = target_entity + if human.get_position() is None: + return None + if human.get_hp() is None or human.get_hp() == 0: + return None + + target_position = human.get_position() + if target_position is None: + return None + if agent_position == target_position: + if isinstance(human, Civilian) and ((human.get_buriedness() or 0) == 0): + return ActionLoad(human.get_entity_id()) + else: + path = path_planning.get_path(agent_position, target_position) + if path is not None and len(path) > 0: + return ActionMove(path) + return None + + if isinstance(target_entity, Area): + if agent_position is None: + return None + path = path_planning.get_path(agent_position, target_entity.get_entity_id()) + if path is not None and len(path) > 0: + return ActionMove(path) + + return None + + def calc_unload( + self, + agent: AmbulanceTeam, + path_planning: PathPlanning, + transport_human: Optional[Human], + target_id: Optional[EntityID], + ) -> Optional[ActionMove | ActionUnload | ActionRest]: + if transport_human is None: + return None + + if not transport_human.get_hp() or transport_human.get_hp() == 0: + return ActionUnload() + + agent_position = agent.get_position() + if agent_position is None: + return None + if target_id is None or transport_human.get_entity_id() == target_id: + position = self.world_info.get_entity(agent_position) + if position is None: + return None + + if isinstance(position, Refuge): + return ActionUnload() + else: + path = self.get_nearest_refuge_path(agent, path_planning) + if path is not None and len(path) > 0: + return ActionMove(path) + + if target_id is None: + return None + + target_entity = self.world_info.get_entity(target_id) + + if isinstance(target_entity, Human): + human = target_entity + human_position = human.get_position() + if human_position is not None: + return self.calc_refuge_action(agent, path_planning, human_position, True) + path = self.get_nearest_refuge_path(agent, path_planning) + if path is not None and len(path) > 0: + return ActionMove(path) + + return None + + def calc_refuge_action( + self, + human: Human, + path_planning: PathPlanning, + target_entity_id: EntityID, + is_unload: bool, + ) -> Optional[ActionMove | ActionUnload | ActionRest]: + position = human.get_position() + if position is None: + return None + refuges = self.world_info.get_entity_ids_of_types([Refuge]) + size = len(refuges) + + if position in refuges: + return ActionUnload() if is_unload else ActionRest() + + first_result = None + while len(refuges) > 0: + path = path_planning.get_path(position, refuges[0]) + + if path is not None and len(path) > 0: + if first_result is None: + first_result = path.copy() + + refuge_id = path[-1] + from_refuge_to_target = path_planning.get_path(refuge_id, target_entity_id) + + if from_refuge_to_target is not None and len(from_refuge_to_target) > 0: + return ActionMove(path) + + refuges.remove(refuge_id) + if size == len(refuges): + break + size = len(refuges) + else: + break + + return ActionMove(first_result) if first_result is not None else None + + def get_nearest_refuge_path( + self, human: Human, path_planning: PathPlanning + ) -> list[EntityID]: + position = human.get_position() + if position is None: + return [] + refuges = self.world_info.get_entity_ids_of_types([Refuge]) + nearest_path = None + + for refuge_id in refuges: + path: list[EntityID] = path_planning.get_path(position, refuge_id) + if len(path) > 0: + if nearest_path is None or len(path) < len(nearest_path): + nearest_path = path + + return nearest_path if nearest_path is not None else [] diff --git a/src/adf_core_python/implement/centralized/default_command_executor_ambulance.py b/src/adf_core_python/implement/centralized/default_command_executor_ambulance.py new file mode 100644 index 00000000..ffd0a7d4 --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_executor_ambulance.py @@ -0,0 +1,292 @@ +from typing import Optional, cast + +from rcrscore.entities import AmbulanceTeam, Area, Civilian, EntityID, Human, Refuge + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorAmbulance(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandAmbulance.ACTION_REST + ACTION_MOVE = CommandAmbulance.ACTION_MOVE + ACTION_RESCUE = CommandAmbulance.ACTION_RESCUE + ACTION_LOAD = CommandAmbulance.ACTION_LOAD + ACTION_UNLOAD = CommandAmbulance.ACTION_UNLOAD + ACTION_AUTONOMY = CommandAmbulance.ACTION_AUTONOMY + + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorAmbulance.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_transport = module_manager.get_extend_action( + "DefaultCommandExecutorAmbulance.ExtendActionTransport", + "adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorAmbulance.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandAmbulance) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_RESCUE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_LOAD: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_UNLOAD: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_transport.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_transport.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_transport.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + + if self._command_type == self.ACTION_LOAD: + self._command_type = self.ACTION_UNLOAD + self._target = None + else: + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_transport.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_transport.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_transport.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + return agent.get_damage() == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + return agent.get_damage() == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_RESCUE: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + return human.get_buriedness() == 0 or human.get_hp() == 0 + case self.ACTION_LOAD: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + if human.get_hp() == 0: + return True + if isinstance(human, Civilian): + self._command_type = self.ACTION_RESCUE + return self._is_command_completed() + position = human.get_position() + if position is not None: + if position in self._world_info.get_entity_ids_of_types([AmbulanceTeam]): + return True + elif isinstance(self._world_info.get_entity(position), Refuge): + return True + return False + + case self.ACTION_UNLOAD: + if self._target is not None: + entity = self._world_info.get_entity(self._target) + if entity is not None and isinstance(entity, Refuge): + if self._target == self._agent_info.get_position_entity_id(): + return False + return self._agent_info.some_one_on_board() is None + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + self._command_type = ( + self._agent_info.some_one_on_board() is None + and self.ACTION_MOVE + or self.ACTION_UNLOAD + ) + return self._is_command_completed() + elif isinstance(target_entity, Human): + human = target_entity + if human.get_hp() == 0: + return True + self._command_type = ( + isinstance(human, Civilian) and self.ACTION_LOAD or self.ACTION_RESCUE + ) + return self._is_command_completed() + return True + case _: + return True diff --git a/src/adf_core_python/implement/centralized/default_command_executor_fire.py b/src/adf_core_python/implement/centralized/default_command_executor_fire.py new file mode 100644 index 00000000..9b0451ad --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_executor_fire.py @@ -0,0 +1,238 @@ +from typing import Optional, cast + +from rcrscore.entities import Area, Civilian, EntityID, Human, Refuge + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorFire(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandAmbulance.ACTION_REST + ACTION_MOVE = CommandAmbulance.ACTION_MOVE + ACTION_RESCUE = CommandAmbulance.ACTION_RESCUE + ACTION_AUTONOMY = CommandAmbulance.ACTION_AUTONOMY + + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorFire.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_fire_rescue = module_manager.get_extend_action( + "DefaultCommandExecutorFire.ExtendActionFireRescue", + "adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorFire.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandAmbulance) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_RESCUE: + if self._target: + self._result = ( + self._action_fire_rescue.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_fire_rescue.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_fire_rescue.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_fire_rescue.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_fire_rescue.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_fire_rescue.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + return agent.get_damage() == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + return agent.get_damage() == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_RESCUE: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + return human.get_buriedness() == 0 or human.get_hp() == 0 + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + self._command_type = self.ACTION_MOVE + return self._is_command_completed() + elif isinstance(target_entity, Human): + human = target_entity + if human.get_hp() == 0: + return True + if isinstance(human, Civilian): + self._command_type = self.ACTION_RESCUE + return self._is_command_completed() + return True + case _: + return True diff --git a/src/adf_core_python/implement/centralized/default_command_executor_police.py b/src/adf_core_python/implement/centralized/default_command_executor_police.py new file mode 100644 index 00000000..94367c9e --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_executor_police.py @@ -0,0 +1,260 @@ +from typing import Optional, cast + +from rcrscore.entities import Area, Blockade, EntityID, Human, Refuge, Road + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorPolice(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandPolice.ACTION_REST + ACTION_MOVE = CommandPolice.ACTION_MOVE + ACTION_CLEAR = CommandPolice.ACTION_CLEAR + ACTION_AUTONOMY = CommandPolice.ACTION_AUTONOMY + + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorPolice.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_clear = module_manager.get_extend_action( + "DefaultCommandExecutorPolice.ExtendActionClear", + "adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorPolice.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandPolice) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_CLEAR: + if self._target: + self._result = ( + self._action_clear.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_clear.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_clear.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_clear.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_clear.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_clear.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + damage = agent.get_damage() + return damage is not None and damage == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + damage = agent.get_damage() + return damage is not None and damage == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_CLEAR: + if self._target is None: + return True + entity = self._world_info.get_entity(self._target) + if isinstance(entity, Road): + blockades = entity.get_blockades() + if blockades is not None: + return len(blockades) == 0 + return self._agent_info.get_position_entity_id() == self._target + return True + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Refuge): + damage = agent.get_damage() + self._command_type = ( + self.ACTION_REST + if damage is not None and damage > 0 + else self.ACTION_CLEAR + ) + return self._is_command_completed() + elif isinstance(target_entity, Area): + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + elif isinstance(target_entity, Human): + if target_entity.get_hp() == 0: + return True + position = target_entity.get_position() + if position is not None and isinstance( + self._world_info.get_entity(position), + Area, + ): + self._target = target_entity.get_position() + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + elif isinstance(target_entity, Blockade): + if target_entity.get_position() is not None: + self._target = target_entity.get_position() + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + return True + case _: + return True diff --git a/src/adf_core_python/implement/centralized/default_command_executor_scout.py b/src/adf_core_python/implement/centralized/default_command_executor_scout.py new file mode 100644 index 00000000..8693cdf8 --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_executor_scout.py @@ -0,0 +1,159 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, EntityID, Human, Refuge, Road + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorScout(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_SCOUT: int = 1 + + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorScout.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._targets: list[EntityID] = [] + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandScout) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + target = command.get_command_target_entity_id() + if target is None: + target = self._agent_info.get_position_entity_id() + if target is None: + return self + + self._command_type = self.ACTION_SCOUT + self._commander = command.get_sender_entity_id() + self._targets = [] + if (scout_distance := command.get_scout_range()) is None: + return self + + for entity in self._world_info.get_entities_of_types([Road, Building]): + if isinstance(entity, Refuge): + continue + if ( + self._world_info.get_distance(target, entity.get_entity_id()) <= scout_distance + ): + self._targets.append(entity.get_entity_id()) + + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) == 0: + return self + agent_position = self._agent_info.get_position_entity_id() + if agent_position is None: + return self + path = self._path_planning.get_path(agent_position, self._targets[0]) + if path is None: + return self + self._result = ActionMove(path) + case _: + return self + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) != 0: + for entity in self._world_info.get_entities_of_types([Road, Building]): + self._targets.remove(entity.get_entity_id()) + return len(self._targets) == 0 + case _: + return True diff --git a/src/adf_core_python/implement/centralized/default_command_executor_scout_police.py b/src/adf_core_python/implement/centralized/default_command_executor_scout_police.py new file mode 100644 index 00000000..00d4f471 --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_executor_scout_police.py @@ -0,0 +1,173 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, EntityID, Human, Refuge, Road + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorScoutPolice(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_SCOUT: int = 1 + + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorScoutPolice.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_clear: ExtendAction = module_manager.get_extend_action( + "DefaultCommandExecutorScoutPolice.ExtendActionClear", + "adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._targets: list[EntityID] = [] + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandScout) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + target = command.get_command_target_entity_id() + if target is None: + target = self._agent_info.get_position_entity_id() + if target is None: + return self + + self._command_type = self.ACTION_SCOUT + self._commander = command.get_sender_entity_id() + self._targets = [] + if (scout_distance := command.get_scout_range()) is None: + return self + + for entity in self._world_info.get_entities_of_types([Road, Building, Refuge]): + if isinstance(entity, Refuge): + continue + if ( + self._world_info.get_distance(target, entity.get_entity_id()) <= scout_distance + ): + self._targets.append(entity.get_entity_id()) + + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) == 0: + return self + agent_position = self._agent_info.get_position_entity_id() + if agent_position is None: + return self + path = self._path_planning.get_path(agent_position, self._targets[0]) + if path is None: + return self + action = ( + self._action_clear.set_target_entity_id(self._targets[0]) + .calculate() + .get_action() + ) + if action is None: + action = ActionMove(path) + self._result = action + case _: + return self + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) != 0: + for entity in self._world_info.get_entities_of_types( + [Road, Building, Refuge] + ): + self._targets.remove(entity.get_entity_id()) + return len(self._targets) == 0 + case _: + return True diff --git a/src/adf_core_python/implement/centralized/default_command_picker_ambulance.py b/src/adf_core_python/implement/centralized/default_command_picker_ambulance.py new file mode 100644 index 00000000..753b1889 --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_picker_ambulance.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from rcrscore.entities import AmbulanceTeam, Area, EntityID, Human + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerAmbulance(CommandPicker): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self.messages: list[CommunicationMessage] = [] + self.scout_distance: int = 40000 + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, AmbulanceTeam): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + command: CommunicationMessage + if isinstance(target, Human): + command = CommandAmbulance( + True, + ambulance_id, + self._agent_info.get_entity_id(), + CommandAmbulance.ACTION_RESCUE, + StandardMessagePriority.NORMAL, + target.get_entity_id(), + ) + self.messages.append(command) + + if isinstance(target, Area): + command = CommandScout( + True, + ambulance_id, + self._agent_info.get_entity_id(), + self.scout_distance, + StandardMessagePriority.NORMAL, + target.get_entity_id(), + ) + self.messages.append(command) + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages diff --git a/src/adf_core_python/implement/centralized/default_command_picker_fire.py b/src/adf_core_python/implement/centralized/default_command_picker_fire.py new file mode 100644 index 00000000..b7ec0381 --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_picker_fire.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from rcrscore.entities import Area, EntityID, FireBrigade, Human + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerFire(CommandPicker): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self.messages: list[CommunicationMessage] = [] + self.scout_distance: int = 40000 + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, FireBrigade): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + command: CommunicationMessage + if isinstance(target, Human): + command = CommandAmbulance( + True, + ambulance_id, + self._agent_info.get_entity_id(), + CommandAmbulance.ACTION_RESCUE, + StandardMessagePriority.NORMAL, + target.get_entity_id(), + ) + self.messages.append(command) + + if isinstance(target, Area): + command = CommandScout( + True, + ambulance_id, + self._agent_info.get_entity_id(), + self.scout_distance, + StandardMessagePriority.NORMAL, + target.get_entity_id(), + ) + self.messages.append(command) + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages diff --git a/src/adf_core_python/implement/centralized/default_command_picker_police.py b/src/adf_core_python/implement/centralized/default_command_picker_police.py new file mode 100644 index 00000000..f6be460c --- /dev/null +++ b/src/adf_core_python/implement/centralized/default_command_picker_police.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from rcrscore.entities import Area, EntityID, PoliceForce + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerPolice(CommandPicker): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self.messages: list[CommunicationMessage] = [] + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, PoliceForce): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + if isinstance(target, Area): + command = CommandPolice( + True, + agent.get_entity_id(), + self._agent_info.get_entity_id(), + CommandPolice.ACTION_AUTONOMY, + StandardMessagePriority.NORMAL, + target.get_entity_id(), + ) + self.messages.append(command) + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages diff --git a/src/adf_core_python/implement/default_loader.py b/src/adf_core_python/implement/default_loader.py new file mode 100644 index 00000000..9b638885 --- /dev/null +++ b/src/adf_core_python/implement/default_loader.py @@ -0,0 +1,57 @@ +from adf_core_python.core.component.abstract_loader import AbstractLoader +from adf_core_python.core.component.tactics.tactics_ambulance_center import ( + TacticsAmbulanceCenter, +) +from adf_core_python.core.component.tactics.tactics_ambulance_team import ( + TacticsAmbulanceTeam, +) +from adf_core_python.core.component.tactics.tactics_fire_brigade import ( + TacticsFireBrigade, +) +from adf_core_python.core.component.tactics.tactics_fire_station import ( + TacticsFireStation, +) +from adf_core_python.core.component.tactics.tactics_police_force import ( + TacticsPoliceForce, +) +from adf_core_python.core.component.tactics.tactics_police_office import ( + TacticsPoliceOffice, +) +from adf_core_python.implement.tactics.default_tactics_ambulance_center import ( + DefaultTacticsAmbulanceCenter, +) +from adf_core_python.implement.tactics.default_tactics_ambulance_team import ( + DefaultTacticsAmbulanceTeam, +) +from adf_core_python.implement.tactics.default_tactics_fire_brigade import ( + DefaultTacticsFireBrigade, +) +from adf_core_python.implement.tactics.default_tactics_fire_station import ( + DefaultTacticsFireStation, +) +from adf_core_python.implement.tactics.default_tactics_police_force import ( + DefaultTacticsPoliceForce, +) +from adf_core_python.implement.tactics.default_tactics_police_office import ( + DefaultTacticsPoliceOffice, +) + + +class DefaultLoader(AbstractLoader): + def get_tactics_ambulance_team(self) -> TacticsAmbulanceTeam: + return DefaultTacticsAmbulanceTeam() + + def get_tactics_fire_brigade(self) -> TacticsFireBrigade: + return DefaultTacticsFireBrigade() + + def get_tactics_police_force(self) -> TacticsPoliceForce: + return DefaultTacticsPoliceForce() + + def get_tactics_ambulance_center(self) -> TacticsAmbulanceCenter: + return DefaultTacticsAmbulanceCenter() + + def get_tactics_fire_station(self) -> TacticsFireStation: + return DefaultTacticsFireStation() + + def get_tactics_police_office(self) -> TacticsPoliceOffice: + return DefaultTacticsPoliceOffice() diff --git a/src/adf_core_python/implement/module/__init__.py b/src/adf_core_python/implement/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/module/algorithm/__init__.py b/src/adf_core_python/implement/module/algorithm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/module/algorithm/a_star_path_planning.py b/src/adf_core_python/implement/module/algorithm/a_star_path_planning.py new file mode 100644 index 00000000..8b2741a9 --- /dev/null +++ b/src/adf_core_python/implement/module/algorithm/a_star_path_planning.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +from rcrscore.entities import Area, Building, Entity, EntityID, Road + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.path_planning import ( + PathPlanning, +) + + +class AStarPathPlanning(PathPlanning): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + entities: list[Entity] = self._world_info.get_entities_of_types([Building, Road]) + self._graph: dict[EntityID, set[EntityID]] = {} + for entity in entities: + if isinstance(entity, Area): + self._graph[entity.get_entity_id()] = set( + neighbor for neighbor in entity.get_neighbors() if neighbor != EntityID(0) + ) + + def get_path( + self, from_entity_id: EntityID, to_entity_id: EntityID + ) -> list[EntityID]: + open_set: set[EntityID] = {from_entity_id} + came_from: dict[EntityID, EntityID] = {} + g_score: dict[EntityID, float] = {from_entity_id: 0.0} + f_score: dict[EntityID, float] = { + from_entity_id: self.heuristic(from_entity_id, to_entity_id) + } + + while open_set: + current: EntityID = min(open_set, key=lambda x: f_score.get(x, float("inf"))) + if current == to_entity_id: + return self.reconstruct_path(came_from, current) + + open_set.remove(current) + for neighbor in self._graph.get(current, []): + tentative_g_score: float = g_score[current] + self.distance(current, neighbor) + if tentative_g_score < g_score.get(neighbor, float("inf")): + came_from[neighbor] = current + g_score[neighbor] = tentative_g_score + f_score[neighbor] = tentative_g_score + self.heuristic(neighbor, to_entity_id) + if neighbor not in open_set: + open_set.add(neighbor) + + return [] + + def get_path_to_multiple_destinations( + self, from_entity_id: EntityID, destination_entity_ids: set[EntityID] + ) -> list[EntityID]: + return [] + + def heuristic(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + # Implement a heuristic function, for example, Euclidean distance + return self._world_info.get_distance(from_entity_id, to_entity_id) + + def distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + # Implement a distance function, for example, Euclidean distance + return self._world_info.get_distance(from_entity_id, to_entity_id) + + def reconstruct_path( + self, came_from: dict[EntityID, EntityID], current: EntityID + ) -> list[EntityID]: + total_path = [current] + while current in came_from: + current = came_from[current] + total_path.append(current) + total_path.reverse() + return total_path + + def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + path: list[EntityID] = self.get_path(from_entity_id, to_entity_id) + distance: float = 0.0 + for i in range(len(path) - 1): + distance += self.distance(path[i], path[i + 1]) + return distance + + def calculate(self) -> AStarPathPlanning: + return self diff --git a/src/adf_core_python/implement/module/algorithm/dijkstra_path_planning.py b/src/adf_core_python/implement/module/algorithm/dijkstra_path_planning.py new file mode 100644 index 00000000..43a01213 --- /dev/null +++ b/src/adf_core_python/implement/module/algorithm/dijkstra_path_planning.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import heapq +from typing import Optional + +from rcrscore.entities import Area, Building, EntityID, Road + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DijkstraPathPlanning(PathPlanning): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self.graph: dict[EntityID, list[tuple[EntityID, float]]] = {} + # グラフの構築 + for area in self._world_info.get_entities_of_types([Road, Building]): + if not isinstance(area, Area): + continue + if (neighbors := area.get_neighbors()) is None: + continue + area_id = area.get_entity_id() + self.graph[area_id] = [ + ( + neighbor, + self._world_info.get_distance(area_id, entity_id2=neighbor), + ) + for neighbor in neighbors + if neighbor.get_value() != 0 + ] + + def calculate(self) -> PathPlanning: + return self + + def get_path( + self, from_entity_id: EntityID, to_entity_id: EntityID + ) -> list[EntityID]: + # ダイクストラ法で最短経路を計算 + queue: list[tuple[float, EntityID]] = [] + heapq.heappush(queue, (0, from_entity_id)) + distance: dict[EntityID, float] = {from_entity_id: 0} + previous: dict[EntityID, Optional[EntityID]] = {from_entity_id: None} + + while queue: + current_distance, current_node = heapq.heappop(queue) + if current_node == to_entity_id: + break + + self._logger.info( + f"current_node: {current_node}, current_entity: {self._world_info.get_entity(current_node)}" + ) + + for neighbor, weight in self.graph[current_node]: + new_distance = current_distance + weight + if neighbor not in distance or new_distance < distance[neighbor]: + distance[neighbor] = new_distance + heapq.heappush(queue, (new_distance, neighbor)) + previous[neighbor] = current_node + + path: list[EntityID] = [] + current_path_node: Optional[EntityID] = to_entity_id + while current_path_node is not None: + path.append(current_path_node) + current_path_node = previous.get(current_path_node) + + return path[::-1] + + def get_path_to_multiple_destinations( + self, from_entity_id: EntityID, destination_entity_ids: set[EntityID] + ) -> list[EntityID]: + open_list = [from_entity_id] + ancestors = {from_entity_id: from_entity_id} + found = False + next_node = None + + while open_list and not found: + next_node = open_list.pop(0) + if self.is_goal(next_node, destination_entity_ids): + found = True + break + + neighbors = self.graph.get(next_node, []) + if not neighbors: + continue + + for neighbor, _ in neighbors: + if self.is_goal(neighbor, destination_entity_ids): + ancestors[neighbor] = next_node + next_node = neighbor + found = True + break + elif neighbor not in ancestors: + open_list.append(neighbor) + ancestors[neighbor] = next_node + + if not found: + return [] + + path: list[EntityID] = [] + current = next_node + while current != from_entity_id: + if current is None: + raise RuntimeError("Found a node with no ancestor! Something is broken.") + path.insert(0, current) + current = ancestors.get(current) + path.insert(0, from_entity_id) + + return path + + def is_goal(self, entity_id: EntityID, target_ids: set[EntityID]) -> bool: + return entity_id in target_ids + + def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float: + path = self.get_path(from_entity_id, to_entity_id) + distance = 0.0 + for i in range(len(path) - 1): + distance += self._world_info.get_distance(path[i], path[i + 1]) + return distance diff --git a/src/adf_core_python/implement/module/algorithm/k_means_clustering.py b/src/adf_core_python/implement/module/algorithm/k_means_clustering.py new file mode 100644 index 00000000..3ccdcf01 --- /dev/null +++ b/src/adf_core_python/implement/module/algorithm/k_means_clustering.py @@ -0,0 +1,160 @@ +import numpy as np +from rcrscore.entities import ( + AmbulanceCenter, + Building, + Entity, + EntityID, + FireStation, + GasStation, + Hydrant, + PoliceOffice, + Refuge, + Road, +) +from rcrscore.urn import EntityURN +from sklearn.cluster import KMeans + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.algorithm.clustering import Clustering + + +class KMeansClustering(Clustering): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + myself = agent_info.get_myself() + if myself is None: + raise RuntimeError("Could not get agent entity") + match myself.get_urn(): + case EntityURN.AMBULANCE_TEAM: + self._cluster_number = int( + scenario_info.get_value( + "scenario.agents.at", + 1, + ) + ) + case EntityURN.POLICE_FORCE: + self._cluster_number = int( + scenario_info.get_value( + "scenario.agents.pf", + 1, + ) + ) + case EntityURN.FIRE_BRIGADE: + self._cluster_number = int( + scenario_info.get_value( + "scenario.agents.fb", + 1, + ) + ) + case _: + self._cluster_number = 1 + + sorted_entities = sorted( + world_info.get_entities_of_types( + [ + myself.__class__, + ] + ), + key=lambda entity: entity.get_entity_id().get_value(), + ) + self.entity_cluster_indices = { + entity.get_entity_id(): idx for idx, entity in enumerate(sorted_entities) + } + + self.cluster_entities: list[list[Entity]] = [] + self.entities: list[Entity] = world_info.get_entities_of_types( + [ + AmbulanceCenter, + FireStation, + GasStation, + Hydrant, + PoliceOffice, + Refuge, + Road, + Building, + ] + ) + + def calculate(self) -> Clustering: + return self + + def precompute(self, precompute_data: PrecomputeData) -> Clustering: + cluster_entities = self.create_cluster(self._cluster_number, self.entities) + precompute_data.write_json_data( + { + "cluster_entities": [ + [entity.get_entity_id().get_value() for entity in cluster] + for cluster in cluster_entities + ] + }, + self.__class__.__name__, + ) + return self + + def resume(self, precompute_data: PrecomputeData) -> Clustering: + data = precompute_data.read_json_data(self.__class__.__name__) + self.cluster_entities = [ + [ + entity + for entity_id in cluster + if (entity := self._world_info.get_entity(EntityID(entity_id))) is not None + ] + for cluster in data["cluster_entities"] + ] + return self + + def get_cluster_number(self) -> int: + return self._cluster_number + + def get_cluster_index(self, entity_id: EntityID) -> int: + return self.entity_cluster_indices.get(entity_id, 0) + + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + if cluster_index >= len(self.cluster_entities): + return [] + return self.cluster_entities[cluster_index] + + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + if cluster_index >= len(self.cluster_entities): + return [] + return [entity.get_entity_id() for entity in self.cluster_entities[cluster_index]] + + def prepare(self) -> Clustering: + super().prepare() + if self.get_count_prepare() > 1: + return self + self.cluster_entities = self.create_cluster(self._cluster_number, self.entities) + return self + + def create_cluster( + self, cluster_number: int, entities: list[Entity] + ) -> list[list[Entity]]: + kmeans = KMeans(n_clusters=cluster_number, random_state=0) + entity_positions: np.ndarray = np.array([]) + for entity in entities: + location1_x, location1_y = entity.get_location() + if location1_x is None or location1_y is None: + continue + entity_positions = np.append(entity_positions, [location1_x, location1_y]) + + kmeans.fit(entity_positions.reshape(-1, 2)) + + clusters: list[list[Entity]] = [[] for _ in range(cluster_number)] + for entity, label in zip(entities, kmeans.labels_): + clusters[label].append(entity) + + return clusters diff --git a/src/adf_core_python/implement/module/communication/__init__.py b/src/adf_core_python/implement/module/communication/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/module/communication/default_channel_subscriber.py b/src/adf_core_python/implement/module/communication/default_channel_subscriber.py new file mode 100644 index 00000000..c9df4b27 --- /dev/null +++ b/src/adf_core_python/implement/module/communication/default_channel_subscriber.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.info.scenario_info import ScenarioInfoKeys +from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + + +class DefaultChannelSubscriber(ChannelSubscriber): + def subscribe( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + agent = world_info.get_entity(agent_info.get_entity_id()) + if agent is None: + return [] + + agent_type = agent.get_urn() + + number_of_channels: int = ( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1) - 1 + ) + + is_platoon: bool = ( + agent_type == EntityURN.FIRE_BRIGADE + or agent_type == EntityURN.POLICE_FORCE + or agent_type == EntityURN.AMBULANCE_TEAM + ) + + max_channel_count: int = ( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_PLATOON, 1) + if is_platoon + else scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_OFFICE, 1 + ) + ) + + channels = [ + self.get_channel_number(agent_type, i, number_of_channels) + for i in range(max_channel_count) + ] + return channels + + @staticmethod + def get_channel_number( + agent_type: EntityURN, channel_index: int, number_of_channels: int + ) -> int: + agent_index = 0 + if agent_type == EntityURN.FIRE_BRIGADE or agent_type == EntityURN.FIRE_STATION: + agent_index = 1 + elif agent_type == EntityURN.POLICE_FORCE or agent_type == EntityURN.POLICE_OFFICE: + agent_index = 2 + elif ( + agent_type == EntityURN.AMBULANCE_TEAM or agent_type == EntityURN.AMBULANCE_CENTER + ): + agent_index = 3 + + index = (3 * channel_index) + agent_index + if (index % number_of_channels) == 0: + index = number_of_channels + else: + index = index % number_of_channels + return index + + +if __name__ == "__main__": + num_channels = 1 + max_channels = 2 + + for i in range(max_channels): + print( + f"FIREBRIGADE-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.FIRE_BRIGADE, i, num_channels)}" + ) + + for i in range(max_channels): + print( + f"POLICE-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.POLICE_OFFICE, i, num_channels)}" + ) + + for i in range(max_channels): + print( + f"AMB-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.AMBULANCE_CENTER, i, num_channels)}" + ) diff --git a/src/adf_core_python/implement/module/communication/default_message_coordinator.py b/src/adf_core_python/implement/module/communication/default_message_coordinator.py new file mode 100644 index 00000000..3428304b --- /dev/null +++ b/src/adf_core_python/implement/module/communication/default_message_coordinator.py @@ -0,0 +1,238 @@ +from typing import Optional + +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, ScenarioInfoKeys +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) +from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, +) +from adf_core_python.implement.module.communication.default_channel_subscriber import ( + DefaultChannelSubscriber, +) + + +class DefaultMessageCoordinator(MessageCoordinator): + def coordinate( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + message_manager: MessageManager, + send_message_list: list[CommunicationMessage], + channel_send_message_list: list[list[CommunicationMessage]], + ) -> None: + police_messages: list[StandardMessage] = [] + ambulance_messages: list[StandardMessage] = [] + fire_brigade_messages: list[StandardMessage] = [] + voice_messages: list[StandardMessage] = [] + + agent_type = self.get_agent_type(agent_info, world_info) + + for msg in send_message_list: + if isinstance(msg, StandardMessage) and not msg.is_wireless_message(): + voice_messages.append(msg) + else: + if isinstance(msg, MessageBuilding): + fire_brigade_messages.append(msg) + elif isinstance(msg, MessageCivilian): + ambulance_messages.append(msg) + elif isinstance(msg, MessageRoad): + fire_brigade_messages.append(msg) + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, CommandAmbulance): + ambulance_messages.append(msg) + elif isinstance(msg, CommandFire): + fire_brigade_messages.append(msg) + elif isinstance(msg, CommandPolice): + police_messages.append(msg) + elif isinstance(msg, CommandScout): + if agent_type == EntityURN.FIRE_STATION: + fire_brigade_messages.append(msg) + elif agent_type == EntityURN.POLICE_OFFICE: + police_messages.append(msg) + elif agent_type == EntityURN.AMBULANCE_CENTER: + ambulance_messages.append(msg) + elif isinstance(msg, MessageReport): + if agent_type == EntityURN.FIRE_BRIGADE: + fire_brigade_messages.append(msg) + elif agent_type == EntityURN.POLICE_FORCE: + police_messages.append(msg) + elif agent_type == EntityURN.AMBULANCE_TEAM: + ambulance_messages.append(msg) + elif isinstance(msg, MessageFireBrigade): + fire_brigade_messages.append(msg) + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, MessagePoliceForce): + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, MessageAmbulanceTeam): + ambulance_messages.append(msg) + police_messages.append(msg) + + if int(scenario_info.get_value("comms.channels.count", 1)) > 1: + channel_size = [0] * (int(scenario_info.get_value("comms.channels.count", 1))) + self.set_send_messages( + scenario_info, + EntityURN.POLICE_FORCE, + agent_info, + world_info, + police_messages, + channel_send_message_list, + channel_size, + ) + self.set_send_messages( + scenario_info, + EntityURN.AMBULANCE_TEAM, + agent_info, + world_info, + ambulance_messages, + channel_send_message_list, + channel_size, + ) + self.set_send_messages( + scenario_info, + EntityURN.FIRE_BRIGADE, + agent_info, + world_info, + fire_brigade_messages, + channel_send_message_list, + channel_size, + ) + + voice_message_low_list = [] + voice_message_normal_list = [] + voice_message_high_list = [] + + for msg in voice_messages: + if isinstance(msg, StandardMessage): + if msg.get_priority() == StandardMessagePriority.LOW: + voice_message_low_list.append(msg) + elif msg.get_priority() == StandardMessagePriority.NORMAL: + voice_message_normal_list.append(msg) + elif msg.get_priority() == StandardMessagePriority.HIGH: + voice_message_high_list.append(msg) + + channel_send_message_list[0].extend(voice_message_high_list) + channel_send_message_list[0].extend(voice_message_normal_list) + channel_send_message_list[0].extend(voice_message_low_list) + + def get_channels_by_agent_type( + self, + agent_type: EntityURN, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + num_channels = ( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1) - 1 + ) + max_channel_count = int( + # scenario_info.get_comms_channels_max_platoon() + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_PLATOON, 1) + if self.is_platoon_agent(agent_info, world_info) + else scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_OFFICE, 1 + ) + ) + channels = [ + DefaultChannelSubscriber.get_channel_number(agent_type, i, num_channels) + for i in range(max_channel_count) + ] + return channels + + def is_platoon_agent(self, agent_info: AgentInfo, world_info: WorldInfo) -> bool: + agent_type = self.get_agent_type(agent_info, world_info) + return agent_type in [ + EntityURN.FIRE_BRIGADE, + EntityURN.POLICE_FORCE, + EntityURN.AMBULANCE_TEAM, + ] + + def get_agent_type( + self, agent_info: AgentInfo, world_info: WorldInfo + ) -> Optional[EntityURN]: + entity = world_info.get_entity(agent_info.get_entity_id()) + if entity is None: + return None + return entity.get_urn() + + def set_send_messages( + self, + scenario_info: ScenarioInfo, + agent_type: EntityURN, + agent_info: AgentInfo, + world_info: WorldInfo, + messages: list[StandardMessage], + channel_send_message_list: list[list[CommunicationMessage]], + channel_size: list[int], + ) -> None: + channels = self.get_channels_by_agent_type( + agent_type, agent_info, world_info, scenario_info + ) + channel_capacities = [ + scenario_info.get_value("comms.channels." + str(channel) + ".bandwidth", 0) + for channel in range( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1) + ) + ] + + sorted_messages = sorted( + messages, key=lambda x: x.get_priority().value, reverse=True + ) + + for message in sorted_messages: + for channel in channels: + if message not in channel_send_message_list[channel] and ( + (channel_size[channel] + message.get_bit_size()) + <= channel_capacities[channel] + ): + channel_size[channel] += message.get_bit_size() + channel_send_message_list[channel].append(message) + break diff --git a/src/adf_core_python/implement/module/complex/__init__.py b/src/adf_core_python/implement/module/complex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/module/complex/default_ambulance_target_allocator.py b/src/adf_core_python/implement/module/complex/default_ambulance_target_allocator.py new file mode 100644 index 00000000..599a75c5 --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_ambulance_target_allocator.py @@ -0,0 +1,153 @@ +from functools import cmp_to_key +from typing import Callable, Optional + +from rcrscore.entities import AmbulanceTeam, Entity, EntityID, Human + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.ambulance_target_allocator import ( + AmbulanceTargetAllocator, +) + + +class DefaultAmbulanceTargetAllocator(AmbulanceTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._priority_humans: set[EntityID] = set() + self._target_humans: set[EntityID] = set() + self._ambulance_team_info_map: dict[ + EntityID, DefaultAmbulanceTargetAllocator.AmbulanceTeamInfo + ] = {} + + def resume(self, precompute_data: PrecomputeData) -> AmbulanceTargetAllocator: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + for entity_id in self._world_info.get_entity_ids_of_types([AmbulanceTeam]): + self._ambulance_team_info_map[entity_id] = self.AmbulanceTeamInfo(entity_id) + return self + + def prepare(self) -> AmbulanceTargetAllocator: + super().prepare() + if self.get_count_prepare() >= 2: + return self + for entity_id in self._world_info.get_entity_ids_of_types([AmbulanceTeam]): + self._ambulance_team_info_map[entity_id] = self.AmbulanceTeamInfo(entity_id) + return self + + def update_info(self, message_manager: MessageManager) -> AmbulanceTargetAllocator: + super().update_info(message_manager) + # TODO: implement after message_manager is implemented + return self + + def calculate(self) -> AmbulanceTargetAllocator: + agents = self._get_action_agents(self._ambulance_team_info_map) + removes = [] + current_time = self._agent_info.get_time() + + for target in self._priority_humans: + if len(agents) > 0: + target_entity = self._world_info.get_entity(target) + if target_entity is not None and isinstance(target_entity, Human): + agents = sorted( + agents, key=cmp_to_key(self._compare_by_distance(target_entity)) + ) + result = agents.pop(0) + info = self._ambulance_team_info_map[result.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target + info.command_time = current_time + self._ambulance_team_info_map[result.get_entity_id()] = info + removes.append(target) + + for r in removes: + self._priority_humans.remove(r) + removes.clear() + + for target in self._target_humans: + if len(agents) > 0: + target_entity = self._world_info.get_entity(target) + if target_entity is not None and isinstance(target_entity, Human): + agents = sorted( + agents, key=cmp_to_key(self._compare_by_distance(target_entity)) + ) + result = agents.pop(0) + info = self._ambulance_team_info_map[result.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target + info.command_time = current_time + self._ambulance_team_info_map[result.get_entity_id()] = info + removes.append(target) + + for r in removes: + self._target_humans.remove(r) + + return self + + def get_result(self) -> dict[EntityID, EntityID]: + return self._convert(self._ambulance_team_info_map) + + def _get_action_agents( + self, + info_map: dict[EntityID, "DefaultAmbulanceTargetAllocator.AmbulanceTeamInfo"], + ) -> list[AmbulanceTeam]: + result = [] + for entity in self._world_info.get_entities_of_types([AmbulanceTeam]): + if isinstance(entity, AmbulanceTeam): + info = info_map[entity.get_entity_id()] + if info is not None and info._can_new_action: + result.append(entity) + return result + + def _compare_by_distance( + self, target_entity: Entity + ) -> Callable[[Entity, Entity], int]: + def _cmp_func(entity_a: Entity, entity_b: Entity) -> int: + distance_a = self._world_info.get_distance( + target_entity.get_entity_id(), entity_a.get_entity_id() + ) + distance_b = self._world_info.get_distance( + target_entity.get_entity_id(), entity_b.get_entity_id() + ) + if distance_a < distance_b: + return -1 + elif distance_a > distance_b: + return 1 + else: + return 0 + + return _cmp_func + + def _convert( + self, + info_map: dict[EntityID, "DefaultAmbulanceTargetAllocator.AmbulanceTeamInfo"], + ) -> dict[EntityID, EntityID]: + result = {} + for entity_id in info_map.keys(): + info = info_map[entity_id] + if info is not None and info._target is not None: + result[entity_id] = info._target + return result + + class AmbulanceTeamInfo: + def __init__(self, entity_id: EntityID) -> None: + self._agent_id: EntityID = entity_id + self._target: Optional[EntityID] = None + self._can_new_action: bool = True + self.command_time: int = -1 diff --git a/src/adf_core_python/implement/module/complex/default_fire_target_allocator.py b/src/adf_core_python/implement/module/complex/default_fire_target_allocator.py new file mode 100644 index 00000000..b4b3a2e2 --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_fire_target_allocator.py @@ -0,0 +1,151 @@ +from functools import cmp_to_key +from typing import Callable, Optional + +from rcrscore.entities import Entity, EntityID, FireBrigade, Human + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.fire_target_allocator import ( + FireTargetAllocator, +) + + +class DefaultFireTargetAllocator(FireTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._priority_humans: set[EntityID] = set() + self._target_humans: set[EntityID] = set() + self._fire_brigade_info_map: dict[ + EntityID, DefaultFireTargetAllocator.FireBrigadeInfo + ] = {} + + def resume(self, precompute_data: PrecomputeData) -> FireTargetAllocator: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + for entity_id in self._world_info.get_entity_ids_of_types([FireBrigade]): + self._fire_brigade_info_map[entity_id] = self.FireBrigadeInfo(entity_id) + return self + + def prepare(self) -> FireTargetAllocator: + super().prepare() + if self.get_count_prepare() >= 2: + return self + for entity_id in self._world_info.get_entity_ids_of_types([FireBrigade]): + self._fire_brigade_info_map[entity_id] = self.FireBrigadeInfo(entity_id) + return self + + def update_info(self, message_manager: MessageManager) -> FireTargetAllocator: + super().update_info(message_manager) + # TODO: implement after message_manager is implemented + return self + + def calculate(self) -> FireTargetAllocator: + agents = self._get_action_agents(self._fire_brigade_info_map) + removes = [] + current_time = self._agent_info.get_time() + + for target in self._priority_humans: + if len(agents) > 0: + target_entity = self._world_info.get_entity(target) + if target_entity is not None and isinstance(target_entity, Human): + agents = sorted( + agents, key=cmp_to_key(self._compare_by_distance(target_entity)) + ) + result = agents.pop(0) + info = self._fire_brigade_info_map[result.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target + info.command_time = current_time + self._fire_brigade_info_map[result.get_entity_id()] = info + removes.append(target) + + for r in removes: + self._priority_humans.remove(r) + removes.clear() + + for target in self._target_humans: + if len(agents) > 0: + target_entity = self._world_info.get_entity(target) + if target_entity is not None and isinstance(target_entity, Human): + agents = sorted( + agents, key=cmp_to_key(self._compare_by_distance(target_entity)) + ) + result = agents.pop(0) + info = self._fire_brigade_info_map[result.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target + info.command_time = current_time + self._fire_brigade_info_map[result.get_entity_id()] = info + removes.append(target) + + for r in removes: + self._target_humans.remove(r) + + return self + + def get_result(self) -> dict[EntityID, EntityID]: + return self._convert(self._fire_brigade_info_map) + + def _get_action_agents( + self, info_map: dict[EntityID, "DefaultFireTargetAllocator.FireBrigadeInfo"] + ) -> list[FireBrigade]: + result = [] + for entity in self._world_info.get_entities_of_types([FireBrigade]): + if isinstance(entity, FireBrigade): + info = info_map[entity.get_entity_id()] + if info is not None and info._can_new_action: + result.append(entity) + return result + + def _compare_by_distance( + self, target_entity: Entity + ) -> Callable[[Entity, Entity], int]: + def _cmp_func(entity_a: Entity, entity_b: Entity) -> int: + distance_a = self._world_info.get_distance( + target_entity.get_entity_id(), entity_a.get_entity_id() + ) + distance_b = self._world_info.get_distance( + target_entity.get_entity_id(), entity_b.get_entity_id() + ) + if distance_a < distance_b: + return -1 + elif distance_a > distance_b: + return 1 + else: + return 0 + + return _cmp_func + + def _convert( + self, info_map: dict[EntityID, "DefaultFireTargetAllocator.FireBrigadeInfo"] + ) -> dict[EntityID, EntityID]: + result = {} + for entity_id in info_map.keys(): + info = info_map[entity_id] + if info is not None and info._target is not None: + result[entity_id] = info._target + return result + + class FireBrigadeInfo: + def __init__(self, entity_id: EntityID) -> None: + self._agent_id: EntityID = entity_id + self._target: Optional[EntityID] = None + self._can_new_action: bool = True + self.command_time: int = -1 diff --git a/src/adf_core_python/implement/module/complex/default_human_detector.py b/src/adf_core_python/implement/module/complex/default_human_detector.py new file mode 100644 index 00000000..56d64a7c --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_human_detector.py @@ -0,0 +1,148 @@ +from typing import Optional, cast + +from rcrscore.entities import Civilian, Entity, EntityID, Human +from rcrscore.urn import EntityURN + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.logger.logger import get_agent_logger + + +class DefaultHumanDetector(HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "DefaultHumanDetector.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + self.register_sub_module(self._clustering) + + self._result: Optional[EntityID] = None + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + def calculate(self) -> HumanDetector: + transport_human: Optional[Human] = self._agent_info.some_one_on_board() + if transport_human is not None: + self._result = transport_human.get_entity_id() + return self + + if self._result is not None: + if not self._is_valid_human(self._result): + self._result = None + + if self._result is None: + self._result = self._select_target() + + return self + + def _select_target(self) -> Optional[EntityID]: + if self._result is not None and self._is_valid_human(self._result): + return self._result + + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + + cluster_valid_human_entities: list[Entity] = [ + entity + for entity in cluster_entities + if self._is_valid_human(entity.get_entity_id()) and isinstance(entity, Civilian) + ] + if len(cluster_valid_human_entities) != 0: + nearest_human_entity = cluster_valid_human_entities[0] + nearest_distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + nearest_human_entity.get_entity_id(), + ) + for entity in cluster_valid_human_entities: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + entity.get_entity_id(), + ) + if distance < nearest_distance: + nearest_distance = distance + nearest_human_entity = entity + return nearest_human_entity.get_entity_id() + + world_valid_human_entities: list[Entity] = [ + entity + for entity in self._world_info.get_entities_of_types([Civilian]) + if self._is_valid_human(entity.get_entity_id()) + ] + if len(world_valid_human_entities) != 0: + nearest_human_entity = world_valid_human_entities[0] + nearest_distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + nearest_human_entity.get_entity_id(), + ) + for entity in world_valid_human_entities: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), + entity.get_entity_id(), + ) + if distance < nearest_distance: + nearest_distance = distance + nearest_human_entity = entity + return nearest_human_entity.get_entity_id() + + return None + + def _is_valid_human(self, target_entity_id: EntityID) -> bool: + target: Optional[Entity] = self._world_info.get_entity(target_entity_id) + if target is None: + return False + if not isinstance(target, Human): + return False + hp: Optional[int] = target.get_hp() + if hp is None or hp <= 0: + return False + buriedness: Optional[int] = target.get_buriedness() + if buriedness is None: + return False + myself = self._agent_info.get_myself() + if myself is None: + return False + if myself.get_urn() == EntityURN.FIRE_BRIGADE and buriedness == 0: + return False + if myself.get_urn() == EntityURN.AMBULANCE_TEAM and buriedness > 0: + return False + damage: Optional[int] = target.get_damage() + if damage is None or damage == 0: + return False + position_entity_id: Optional[EntityID] = target.get_position() + if position_entity_id is None: + return False + position: Optional[Entity] = self._world_info.get_entity(position_entity_id) + if position is None: + return False + urn: EntityURN = position.get_urn() + if urn == EntityURN.REFUGE or urn == EntityURN.AMBULANCE_TEAM: + return False + + return True + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result diff --git a/src/adf_core_python/implement/module/complex/default_police_target_allocator.py b/src/adf_core_python/implement/module/complex/default_police_target_allocator.py new file mode 100644 index 00000000..2244d82b --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_police_target_allocator.py @@ -0,0 +1,191 @@ +from functools import cmp_to_key +from typing import Callable, Optional, cast + +from rcrscore.entities import ( + Building, + Entity, + EntityID, + GasStation, + PoliceForce, + Refuge, + Road, +) + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.police_target_allocator import ( + PoliceTargetAllocator, +) + + +class DefaultPoliceTargetAllocator(PoliceTargetAllocator): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._priority_areas: set[EntityID] = set() + self._target_areas: set[EntityID] = set() + self._agent_info_map: dict[ + EntityID, DefaultPoliceTargetAllocator.PoliceForceInfo + ] = {} + + def resume(self, precompute_data: PrecomputeData) -> PoliceTargetAllocator: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + + for entity_id in self._world_info.get_entity_ids_of_types([PoliceForce]): + self._agent_info_map[entity_id] = self.PoliceForceInfo(entity_id) + for entity in self._world_info.get_entities_of_types( + [Refuge, Building, GasStation] + ): + building: Building = cast(Building, entity) + for entity_id in building.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + for entity in self._world_info.get_entities_of_types([Refuge]): + refuge: Refuge = cast(Refuge, entity) + for entity_id in refuge.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_areas.add(entity_id) + return self + + def prepare(self) -> PoliceTargetAllocator: + super().prepare() + if self.get_count_prepare() >= 2: + return self + + for entity_id in self._world_info.get_entity_ids_of_types([PoliceForce]): + self._agent_info_map[entity_id] = self.PoliceForceInfo(entity_id) + + for entity in self._world_info.get_entities_of_types( + [Refuge, Building, GasStation] + ): + building: Building = cast(Building, entity) + for entity_id in building.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + for entity in self._world_info.get_entities_of_types([Refuge]): + refuge: Refuge = cast(Refuge, entity) + for entity_id in refuge.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_areas.add(entity_id) + + return self + + def update_info(self, message_manager: MessageManager) -> PoliceTargetAllocator: + super().update_info(message_manager) + # TODO: implement after message_manager is implemented + return self + + def calculate(self) -> PoliceTargetAllocator: + agents = self._get_action_agents(self._agent_info_map) + removes = [] + current_time = self._agent_info.get_time() + + for target in self._priority_areas: + if len(agents) > 0: + target_entity = self._world_info.get_entity(target) + if target_entity is not None: + agents = sorted( + agents, key=cmp_to_key(self._compare_by_distance(target_entity)) + ) + selected_agent = agents.pop(0) + info = self._agent_info_map[selected_agent.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target + info.command_time = current_time + self._agent_info_map[selected_agent.get_entity_id()] = info + removes.append(target) + + for r in removes: + self._priority_areas.remove(r) + + areas = [] + for target in self._target_areas: + target_entity = self._world_info.get_entity(target) + if target_entity is not None: + areas.append(target_entity) + + for agent in agents: + if len(areas) > 0: + areas.sort(key=cmp_to_key(self._compare_by_distance(agent))) + target_area: Entity = areas.pop(0) + self._target_areas.remove(target_area.get_entity_id()) + info = self._agent_info_map[agent.get_entity_id()] + if info is not None: + info._can_new_action = False + info._target = target_area.get_entity_id() + info.command_time = current_time + self._agent_info_map[agent.get_entity_id()] = info + + return self + + def get_result(self) -> dict[EntityID, EntityID]: + return self._convert(self._agent_info_map) + + def _get_action_agents( + self, info_map: dict[EntityID, "DefaultPoliceTargetAllocator.PoliceForceInfo"] + ) -> list[PoliceForce]: + result = [] + for entity in self._world_info.get_entities_of_types([PoliceForce]): + if isinstance(entity, PoliceForce): + info = info_map[entity.get_entity_id()] + if info is not None and info._can_new_action: + result.append(entity) + return result + + def _compare_by_distance( + self, target_entity: Entity + ) -> Callable[[Entity, Entity], int]: + def _cmp_func(entity_a: Entity, entity_b: Entity) -> int: + distance_a = self._world_info.get_distance( + target_entity.get_entity_id(), entity_a.get_entity_id() + ) + distance_b = self._world_info.get_distance( + target_entity.get_entity_id(), entity_b.get_entity_id() + ) + if distance_a < distance_b: + return -1 + elif distance_a > distance_b: + return 1 + else: + return 0 + + return _cmp_func + + def _convert( + self, info_map: dict[EntityID, "DefaultPoliceTargetAllocator.PoliceForceInfo"] + ) -> dict[EntityID, EntityID]: + result: dict[EntityID, EntityID] = {} + for entity_id in info_map.keys(): + info = info_map[entity_id] + if info is not None and info._target is not None: + result[entity_id] = info._target + return result + + class PoliceForceInfo: + def __init__(self, entity_id: EntityID) -> None: + self._agent_id: EntityID = entity_id + self._target: Optional[EntityID] = None + self._can_new_action: bool = True + self.command_time: int = -1 diff --git a/src/adf_core_python/implement/module/complex/default_road_detector.py b/src/adf_core_python/implement/module/complex/default_road_detector.py new file mode 100644 index 00000000..40655c4b --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_road_detector.py @@ -0,0 +1,152 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, EntityID, GasStation, Refuge, Road + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.algorithm.path_planning import ( + PathPlanning, +) +from adf_core_python.core.component.module.complex.road_detector import RoadDetector + + +class DefaultRoadDetector(RoadDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultRoadDetector.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self.register_sub_module(self._path_planning) + self._result: Optional[EntityID] = None + + def precompute(self, precompute_data: PrecomputeData) -> RoadDetector: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> RoadDetector: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + + self._target_areas: set[EntityID] = set() + entities = self._world_info.get_entities_of_types([Refuge, Building, GasStation]) + for entity in entities: + if not isinstance(entity, Building): + continue + for entity_id in entity.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + self._priority_roads = set() + for entity in self._world_info.get_entities_of_types([Refuge]): + if not isinstance(entity, Building): + continue + for entity_id in entity.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_roads.add(entity_id) + + return self + + def prepare(self) -> RoadDetector: + super().prepare() + if self.get_count_prepare() >= 2: + return self + + self._target_areas = set() + entities = self._world_info.get_entities_of_types([Refuge, Building, GasStation]) + for entity in entities: + building: Building = cast(Building, entity) + for entity_id in building.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._target_areas.add(entity_id) + + self._priority_roads = set() + for entity in self._world_info.get_entities_of_types([Refuge]): + refuge: Refuge = cast(Refuge, entity) + for entity_id in refuge.get_neighbors(): + neighbor = self._world_info.get_entity(entity_id) + if isinstance(neighbor, Road): + self._priority_roads.add(entity_id) + + return self + + def update_info(self, message_manager: MessageManager) -> RoadDetector: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + if self._result is not None: + if self._agent_info.get_position_entity_id == self._result: + entity = self._world_info.get_entity(self._result) + if isinstance(entity, Building): + self._result = None + elif isinstance(entity, Road): + road = entity + if road.get_blockades() == []: + self._target_areas.remove(self._result) + self._result = None + + return self + + def calculate(self) -> RoadDetector: + if self._result is None: + position_entity_id = self._agent_info.get_position_entity_id() + if position_entity_id is None: + return self + if position_entity_id in self._target_areas: + self._result = position_entity_id + return self + remove_list = [] + for entity_id in self._priority_roads: + if entity_id not in self._target_areas: + remove_list.append(entity_id) + + self._priority_roads = self._priority_roads - set(remove_list) + if len(self._priority_roads) > 0: + agent_position = self._agent_info.get_position_entity_id() + if agent_position is None: + return self + _nearest_target_area = agent_position + _nearest_distance = float("inf") + for target_area in self._target_areas: + if ( + self._world_info.get_distance(agent_position, target_area) + < _nearest_distance + ): + _nearest_target_area = target_area + _nearest_distance = self._world_info.get_distance( + agent_position, target_area + ) + path: list[EntityID] = self._path_planning.get_path( + agent_position, _nearest_target_area + ) + if path is not None and len(path) > 0: + self._result = path[-1] + + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result diff --git a/src/adf_core_python/implement/module/complex/default_search.py b/src/adf_core_python/implement/module/complex/default_search.py new file mode 100644 index 00000000..9d303ed2 --- /dev/null +++ b/src/adf_core_python/implement/module/complex/default_search.py @@ -0,0 +1,104 @@ +from typing import Optional, cast + +from rcrscore.entities import Building, Entity, EntityID, Refuge + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.logger.logger import get_agent_logger + + +class DefaultSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._unreached_building_ids: set[EntityID] = set() + self._result: Optional[EntityID] = None + + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "DefaultSearch.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultSearch.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self._logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self.register_sub_module(self._clustering) + self.register_sub_module(self._path_planning) + + def update_info(self, message_manager: MessageManager) -> Search: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + + self._logger.debug( + f"unreached_building_ids: {[str(id) for id in self._unreached_building_ids]}" + ) + + searched_building_id = self._agent_info.get_position_entity_id() + if searched_building_id is not None: + self._unreached_building_ids.discard(searched_building_id) + + if len(self._unreached_building_ids) == 0: + self._unreached_building_ids = self._get_search_targets() + + return self + + def calculate(self) -> Search: + nearest_building_id: Optional[EntityID] = None + nearest_distance: Optional[float] = None + for building_id in self._unreached_building_ids: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), building_id + ) + if nearest_distance is None or distance < nearest_distance: + nearest_building_id = building_id + nearest_distance = distance + self._result = nearest_building_id + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result + + def _get_search_targets(self) -> set[EntityID]: + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + building_entity_ids: list[EntityID] = [ + entity.get_entity_id() + for entity in cluster_entities + if isinstance(entity, Building) and not isinstance(entity, Refuge) + ] + + return set(building_entity_ids) diff --git a/src/adf_core_python/implement/tactics/__init__.py b/src/adf_core_python/implement/tactics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/adf_core_python/implement/tactics/default_tactics_ambulance_center.py b/src/adf_core_python/implement/tactics/default_tactics_ambulance_center.py new file mode 100644 index 00000000..f7108a53 --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_ambulance_center.py @@ -0,0 +1,99 @@ +from typing import cast + +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) +from adf_core_python.core.component.tactics.tactics_ambulance_center import ( + TacticsAmbulanceCenter, +) + + +class DefaultTacticsAmbulanceCenter(TacticsAmbulanceCenter): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self._allocator: TargetAllocator = cast( + TargetAllocator, + module_manager.get_module( + "DefaultTacticsAmbulanceCenter.TargetAllocator", + "adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator", + ), + ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsAmbulanceCenter.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_ambulance.DefaultCommandPickerAmbulance", + ) + self.register_module(self._allocator) + self.register_command_picker(self._picker) + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result).calculate().get_result() + ): + message_manager.add_message(message) diff --git a/src/adf_core_python/implement/tactics/default_tactics_ambulance_team.py b/src/adf_core_python/implement/tactics/default_tactics_ambulance_team.py new file mode 100644 index 00000000..2ab674b2 --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_ambulance_team.py @@ -0,0 +1,198 @@ +from typing import Optional, cast + +from rcrscore.entities import AmbulanceTeam + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.component.tactics.tactics_ambulance_team import ( + TacticsAmbulanceTeam, +) + + +class DefaultTacticsAmbulanceTeam(TacticsAmbulanceTeam): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + # world_info.index_class() + super().initialize( + agent_info, + world_info, + scenario_info, + module_manager, + precompute_data, + message_manager, + develop_data, + ) + + self._search: Search = cast( + Search, + module_manager.get_module( + "DefaultTacticsAmbulanceTeam.Search", + "adf_core_python.implement.module.complex.default_search.DefaultSearch", + ), + ) + self._human_detector: HumanDetector = cast( + HumanDetector, + module_manager.get_module( + "DefaultTacticsAmbulanceTeam.HumanDetector", + "adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector", + ), + ) + self._action_transport = module_manager.get_extend_action( + "DefaultTacticsAmbulanceTeam.ExtendActionTransport", + "adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport", + ) + self._action_ext_move = module_manager.get_extend_action( + "DefaultTacticsAmbulanceTeam.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + self._command_executor_ambulance = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorAmbulance", + "adf_core_python.implement.centralized.default_command_executor_ambulance.DefaultCommandExecutorAmbulance", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout", + ) + + self.register_module(self._search) + self.register_module(self._human_detector) + self.register_action(self._action_transport) + self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_ambulance) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> Action: + self.reset_count() + self.module_update_info(message_manager) + + agent: AmbulanceTeam = cast(AmbulanceTeam, agent_info.get_myself()) # noqa: F841 + entity_id = agent_info.get_entity_id() # noqa: F841 + + self._logger.debug( + f"received messages: {[str(message) for message in message_manager.get_received_message_list()]}, help: {message_manager.get_heard_agent_help_message_count()}" + ) + + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_scout.set_command(message) + if isinstance(message, CommandAmbulance): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_ambulance.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandAmbulance): + action = self._command_executor_ambulance.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + + target_entity_id = self._human_detector.calculate().get_target_entity_id() + self._logger.debug( + f"human detector target_entity_id: {target_entity_id}", + time=agent_info.get_time(), + ) + if target_entity_id is not None: + action = ( + self._action_transport.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + target_entity_id = self._search.calculate().get_target_entity_id() + self._logger.debug( + f"search target_entity_id: {target_entity_id}", time=agent_info.get_time() + ) + if target_entity_id is not None: + action = ( + self._action_ext_move.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + return ActionRest() diff --git a/src/adf_core_python/implement/tactics/default_tactics_fire_brigade.py b/src/adf_core_python/implement/tactics/default_tactics_fire_brigade.py new file mode 100644 index 00000000..5b9b8dbb --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_fire_brigade.py @@ -0,0 +1,194 @@ +from typing import Optional, cast + +from rcrscore.entities import FireBrigade + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.human_detector import HumanDetector +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.component.tactics.tactics_fire_brigade import ( + TacticsFireBrigade, +) + + +class DefaultTacticsFireBrigade(TacticsFireBrigade): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + # world_info.index_class() + super().initialize( + agent_info, + world_info, + scenario_info, + module_manager, + precompute_data, + message_manager, + develop_data, + ) + + self._search: Search = cast( + Search, + module_manager.get_module( + "DefaultTacticsFireBrigade.Search", + "adf_core_python.implement.module.complex.default_search.DefaultSearch", + ), + ) + self._human_detector: HumanDetector = cast( + HumanDetector, + module_manager.get_module( + "DefaultTacticsFireBrigade.HumanDetector", + "adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector", + ), + ) + self._action_rescue = module_manager.get_extend_action( + "DefaultTacticsFireBrigade.ExtendActionRescue", + "adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue", + ) + self._action_ext_move = module_manager.get_extend_action( + "DefaultTacticsAmbulanceTeam.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + self._command_executor_fire = module_manager.get_command_executor( + "DefaultTacticsFireBrigade.CommandExecutorFire", + "adf_core_python.implement.centralized.default_command_executor_fire.DefaultCommandExecutorFire", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout", + ) + + self.register_module(self._search) + self.register_module(self._human_detector) + self.register_action(self._action_rescue) + self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_fire) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> Action: + self.reset_count() + self.module_update_info(message_manager) + + agent: FireBrigade = cast(FireBrigade, agent_info.get_myself()) # noqa: F841 + entity_id = agent_info.get_entity_id() # noqa: F841 + + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_scout.set_command(command=message) + if isinstance(message, CommandFire): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_fire.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandFire): + action = self._command_executor_fire.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + + target_entity_id = self._human_detector.calculate().get_target_entity_id() + self._logger.debug( + f"human detector target_entity_id: {target_entity_id}", + time=agent_info.get_time(), + ) + if target_entity_id is not None: + action = ( + self._action_rescue.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + target_entity_id = self._search.calculate().get_target_entity_id() + self._logger.debug( + f"search target_entity_id: {target_entity_id}", time=agent_info.get_time() + ) + if target_entity_id is not None: + action = ( + self._action_ext_move.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + return ActionRest() diff --git a/src/adf_core_python/implement/tactics/default_tactics_fire_station.py b/src/adf_core_python/implement/tactics/default_tactics_fire_station.py new file mode 100644 index 00000000..099d2b18 --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_fire_station.py @@ -0,0 +1,99 @@ +from typing import cast + +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) +from adf_core_python.core.component.tactics.tactics_fire_station import ( + TacticsFireStation, +) + + +class DefaultTacticsFireStation(TacticsFireStation): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self._allocator: TargetAllocator = cast( + TargetAllocator, + module_manager.get_module( + "DefaultTacticsFireStation.TargetAllocator", + "adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator", + ), + ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsFireStation.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_fire.DefaultCommandPickerFire", + ) + self.register_module(self._allocator) + self.register_command_picker(self._picker) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result).calculate().get_result() + ): + message_manager.add_message(message) diff --git a/src/adf_core_python/implement/tactics/default_tactics_police_force.py b/src/adf_core_python/implement/tactics/default_tactics_police_force.py new file mode 100644 index 00000000..3e262168 --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_police_force.py @@ -0,0 +1,200 @@ +from typing import Optional, cast + +from rcrscore.entities import PoliceForce + +from adf_core_python.core.agent.action.action import Action +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.module.complex.road_detector import RoadDetector +from adf_core_python.core.component.module.complex.search import Search +from adf_core_python.core.component.tactics.tactics_police_force import ( + TacticsPoliceForce, +) + + +class DefaultTacticsPoliceForce(TacticsPoliceForce): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + # world_info.index_class() + super().initialize( + agent_info, + world_info, + scenario_info, + module_manager, + precompute_data, + message_manager, + develop_data, + ) + # self._clear_distance = int( + # scenario_info.get_value("clear.repair.distance", "null") + # ) + + self._search: Search = cast( + Search, + module_manager.get_module( + "DefaultTacticsPoliceForce.Search", + "adf_core_python.implement.module.complex.default_search.DefaultSearch", + ), + ) + self._road_detector: RoadDetector = cast( + RoadDetector, + module_manager.get_module( + "DefaultTacticsPoliceForce.RoadDetector", + "adf_core_python.core.component.module.complex.road_detector.RoadDetector", + ), + ) + self._action_ext_clear = module_manager.get_extend_action( + "DefaultTacticsPoliceForce.ExtendActionClear", + "adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear", + ) + self._action_ext_move = module_manager.get_extend_action( + "DefaultTacticsPoliceForce.ExtendActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + self._command_executor_police = module_manager.get_command_executor( + "DefaultTacticsPoliceForce.CommandExecutorPolice", + "adf_core_python.implement.centralized.default_command_executor_police.DefaultCommandExecutorPolice", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsPoliceForce.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout_police.DefaultCommandExecutorScoutPolice", + ) + + self.register_module(self._search) + self.register_module(self._road_detector) + self.register_action(self._action_ext_clear) + self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_police) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> Action: + self.reset_count() + self.module_update_info(message_manager) + + agent: PoliceForce = cast(PoliceForce, agent_info.get_myself()) # noqa: F841 + entity_id = agent_info.get_entity_id() # noqa: F841 + + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_scout.set_command(command=message) + if isinstance(message, CommandPolice): + if message.get_command_executor_agent_entity_id() == agent_info.get_entity_id(): + self._recent_command = message + self._command_executor_police.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandFire): + action = self._command_executor_police.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + + target_entity_id = self._road_detector.calculate().get_target_entity_id() + self._logger.debug( + f"road detector target_entity_id: {target_entity_id}", + time=agent_info.get_time(), + ) + if target_entity_id is not None: + action = ( + self._action_ext_clear.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + target_entity_id = self._search.calculate().get_target_entity_id() + self._logger.debug( + f"search target_entity_id: {target_entity_id}", time=agent_info.get_time() + ) + if target_entity_id is not None: + action = ( + self._action_ext_move.set_target_entity_id(target_entity_id) + .calculate() + .get_action() + ) + if action is not None: + self._logger.debug(f"action: {action}", time=agent_info.get_time()) + return action + + return ActionRest() diff --git a/src/adf_core_python/implement/tactics/default_tactics_police_office.py b/src/adf_core_python/implement/tactics/default_tactics_police_office.py new file mode 100644 index 00000000..d830de2a --- /dev/null +++ b/src/adf_core_python/implement/tactics/default_tactics_police_office.py @@ -0,0 +1,99 @@ +from typing import cast + +from rcrscore.entities import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.module.complex.target_allocator import ( + TargetAllocator, +) +from adf_core_python.core.component.tactics.tactics_police_office import ( + TacticsPoliceOffice, +) + + +class DefaultTacticsPoliceOffice(TacticsPoliceOffice): + def initialize( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self._allocator: TargetAllocator = cast( + TargetAllocator, + module_manager.get_module( + "DefaultTacticsPoliceOffice.TargetAllocator", + "adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator", + ), + ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsPoliceOffice.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_police.DefaultCommandPickerPolice", + ) + self.register_module(self._allocator) + self.register_command_picker(self._picker) + + def resume( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_resume(precompute_data) + + def precompute( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_precompute(precompute_data) + + def prepare( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + develop_data: DevelopData, + ) -> None: + self.module_prepare() + + def think( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + precompute_data: PrecomputeData, + message_manager: MessageManager, + develop_data: DevelopData, + ) -> None: + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result).calculate().get_result() + ): + message_manager.add_message(message) diff --git a/src/adf_core_python/launcher.py b/src/adf_core_python/launcher.py new file mode 100644 index 00000000..e69b17a7 --- /dev/null +++ b/src/adf_core_python/launcher.py @@ -0,0 +1,145 @@ +import argparse +import resource + +from adf_core_python.core.config.config import Config +from adf_core_python.core.launcher.agent_launcher import AgentLauncher +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.logger.logger import configure_logger, get_logger + + +class Launcher: + def __init__( + self, + launcher_config_file: str, + ) -> None: + try: + resource.setrlimit(resource.RLIMIT_NOFILE, (8192, 1048576)) + except Exception as e: + print( + f"Failed to set resource limit: {e}. " + "This may cause issues with the number of open files." + ) + + self.launcher_config = Config(launcher_config_file) + + parser = argparse.ArgumentParser(description="Agent Launcher") + + parser.add_argument( + "--host", + type=str, + help="host name(Default: localhost)", + metavar="", + ) + parser.add_argument( + "--port", + type=int, + help="port number(Default: 27931)", + metavar="", + ) + parser.add_argument( + "-a", + "--ambulanceteam", + type=int, + help="number of ambulance agents(Default: all ambulance)", + metavar="", + ) + parser.add_argument( + "-f", + "--firebrigade", + type=int, + help="number of firebrigade agents(Default: all firebrigade)", + metavar="", + ) + parser.add_argument( + "-p", + "--policeforce", + type=int, + help="number of policeforce agents(Default: all policeforce)", + metavar="", + ) + parser.add_argument( + "-ac", + "--ambulancecenter", + type=int, + help="number of ambulance center agents(Default: all ambulance center)", + metavar="", + ) + parser.add_argument( + "-fs", + "--firestation", + type=int, + help="number of fire station agents(Default: all fire station)", + metavar="", + ) + parser.add_argument( + "-po", + "--policeoffice", + type=int, + help="number of police office agents(Default: all police office)", + metavar="", + ) + parser.add_argument( + "--precompute", + action="store_true", + help="precompute flag", + ) + parser.add_argument( + "--timeout", + type=int, + help="timeout in seconds", + metavar="", + ) + parser.add_argument("--debug", action="store_true", help="debug flag") + parser.add_argument( + "--java", + action="store_true", + help="using java module flag", + ) + args = parser.parse_args() + + config_map = { + ConfigKey.KEY_KERNEL_HOST: args.host, + ConfigKey.KEY_KERNEL_PORT: args.port, + ConfigKey.KEY_AMBULANCE_TEAM_COUNT: args.ambulanceteam, + ConfigKey.KEY_FIRE_BRIGADE_COUNT: args.firebrigade, + ConfigKey.KEY_POLICE_FORCE_COUNT: args.policeforce, + ConfigKey.KEY_AMBULANCE_CENTRE_COUNT: args.ambulancecenter, + ConfigKey.KEY_FIRE_STATION_COUNT: args.firestation, + ConfigKey.KEY_POLICE_OFFICE_COUNT: args.policeoffice, + ConfigKey.KEY_PRECOMPUTE: args.precompute, + ConfigKey.KEY_KERNEL_TIMEOUT: args.timeout, + ConfigKey.KEY_DEBUG_FLAG: args.debug, + ConfigKey.KEY_GATEWAY_FLAG: args.java, + } + + for key, value in config_map.items(): + if value is not None: + self.launcher_config.set_value(key, value) + + configure_logger() + self.logger = get_logger(__name__) + + self.logger.debug(f"launcher_config: {self.launcher_config}") + + def launch(self) -> None: + agent_launcher: AgentLauncher = AgentLauncher( + self.launcher_config, + ) + agent_launcher.init_connector() + + try: + agent_launcher.launch() + except KeyboardInterrupt: + self.logger.info("Agent launcher interrupted") + except Exception as e: + self.logger.exception("Agent launcher failed", exc_info=e) + raise e + self.logger.info("Agent launcher finished") + + +if __name__ == "__main__": + launcher = Launcher( + "config/launcher.yaml", + ) + + launcher.launch() diff --git a/tests/core/agent/config/module.yaml b/tests/core/agent/config/module.yaml new file mode 100644 index 00000000..54c54a9b --- /dev/null +++ b/tests/core/agent/config/module.yaml @@ -0,0 +1,123 @@ +## DefaultTacticsAmbulanceTeam +DefaultTacticsAmbulanceTeam: + HumanDetector: sample_team.module.complex.SampleHumanDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionTransport: adf_core_python.implement.action.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorAmbulance: adf_core_python.implement.centralized.DefaultCommandExecutorAmbulance + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + +## DefaultTacticsFireBrigade +DefaultTacticsFireBrigade: + HumanDetector: sample_team.module.complex.SampleHumanDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionRescue: adf_core_python.implement.action.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorFire: adf_core_python.implement.centralized.DefaultCommandExecutorFire + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + +## DefaultTacticsPoliceForce +DefaultTacticsPoliceForce: + RoadDetector: sample_team.module.complex.SampleRoadDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorPolice: adf_core_python.implement.centralized.DefaultCommandExecutorPolice + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScoutPolice + +## DefaultTacticsAmbulanceCentre +DefaultTacticsAmbulanceCentre: + TargetAllocator: sample_team.module.complex.SampleAmbulanceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerAmbulance + +## DefaultTacticsFireStation +DefaultTacticsFireStation: + TargetAllocator: sample_team.module.complex.SampleFireTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerFire + +## DefaultTacticsPoliceOffice +DefaultTacticsPoliceOffice: + TargetAllocator: sample_team.module.complex.SamplePoliceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerPolice + +## SampleSearch +SampleSearch: + PathPlanning: + Ambulance: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Fire: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Police: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Clustering: + Ambulance: adf_core_python.implement.module.algorithm.KMeansClustering + Fire: adf_core_python.implement.module.algorithm.KMeansClustering + Police: adf_core_python.implement.module.algorithm.KMeansClustering + +## SampleBuildDetector +SampleBuildingDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + +## SampleRoadDetector +SampleRoadDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## SampleHumanDetector +SampleHumanDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + +## DefaultExtendActionClear +DefaultExtendActionClear: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionFireFighting +DefaultExtendActionFireFighting: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionRescue +DefaultExtendActionRescue: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionMove +DefaultExtendActionMove: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionTransport +DefaultExtendActionTransport: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultCommandExecutorAmbulance +DefaultCommandExecutorAmbulance: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionTransport: adf_core_python.implement.action.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorFire +DefaultCommandExecutorFire: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + EtxActionFireRescue: adf_core_python.implement.action.DefaultExtendActionRescue + EtxActionFireFighting: adf_core_python.implement.action.DefaultExtendActionFireFighting + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorPolice +DefaultCommandExecutorPolice: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorScout +DefaultCommandExecutorScout: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultCommandExecutorScoutPolice +DefaultCommandExecutorScoutPolice: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + +## MessageManager +MessageManager: + PlatoonChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber + CenterChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber + PlatoonMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator + CenterMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator + +## VisualDebug +VisualDebug: true diff --git a/tests/core/agent/config/test_module_config.py b/tests/core/agent/config/test_module_config.py new file mode 100644 index 00000000..ffb8ca54 --- /dev/null +++ b/tests/core/agent/config/test_module_config.py @@ -0,0 +1,48 @@ +import os + +import pytest + +from adf_core_python.core.agent.config.module_config import ModuleConfig + + +class TestModuleConfig: + def test_can_read_from_yaml(self) -> None: + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_file_path = os.path.join(script_dir, "module.yaml") + config = ModuleConfig(config_file_path) + assert ( + config.get_value("DefaultTacticsPoliceOffice.TargetAllocator") + == "sample_team.module.complex.SamplePoliceTargetAllocator" + ) + assert ( + config.get_value("DefaultTacticsPoliceOffice.CommandPicker") + == "adf_core_python.implement.centralized.DefaultCommandPickerPolice" + ) + assert ( + config.get_value("SampleSearch.PathPlanning.Ambulance") + == "adf_core_python.implement.module.algorithm.DijkstraPathPlanning" + ) + assert ( + config.get_value("SampleSearch.PathPlanning.Fire") + == "adf_core_python.implement.module.algorithm.DijkstraPathPlanning" + ) + assert ( + config.get_value("SampleSearch.PathPlanning.Police") + == "adf_core_python.implement.module.algorithm.DijkstraPathPlanning" + ) + assert ( + config.get_value("SampleSearch.Clustering.Ambulance") + == "adf_core_python.implement.module.algorithm.KMeansClustering" + ) + assert ( + config.get_value("SampleSearch.Clustering.Fire") + == "adf_core_python.implement.module.algorithm.KMeansClustering" + ) + assert ( + config.get_value("SampleSearch.Clustering.Police") + == "adf_core_python.implement.module.algorithm.KMeansClustering" + ) + + def test_if_file_not_found(self) -> None: + with pytest.raises(FileNotFoundError): + ModuleConfig("not_found.yaml") diff --git a/tests/core/agent/develop/develop.json b/tests/core/agent/develop/develop.json new file mode 100644 index 00000000..3e2918f2 --- /dev/null +++ b/tests/core/agent/develop/develop.json @@ -0,0 +1,12 @@ +{ + "string" : "test", + "number" : 1, + "boolean" : true, + "dict" : { + "test" : "test" + }, + "array" : [ + "test", + "test" + ] +} diff --git a/tests/core/agent/develop/test_develop.py b/tests/core/agent/develop/test_develop.py new file mode 100644 index 00000000..9501be4d --- /dev/null +++ b/tests/core/agent/develop/test_develop.py @@ -0,0 +1,22 @@ +import os + +import pytest + +from adf_core_python.core.agent.develop.develop_data import DevelopData + + +class TestDevelopData: + def test_can_read_from_yaml(self) -> None: + script_dir = os.path.dirname(os.path.abspath(__file__)) + develop_file_path = os.path.join(script_dir, "develop.json") + develop_data = DevelopData(True, develop_file_path) + + assert develop_data.get_value("string") == "test" + assert develop_data.get_value("number") == 1 + assert develop_data.get_value("boolean") is True + assert develop_data.get_value("dict") == {"test": "test"} + assert develop_data.get_value("array") == ["test", "test"] + + def test_if_file_not_found(self) -> None: + with pytest.raises(FileNotFoundError): + DevelopData(True, "not_found.json") diff --git a/tests/core/agent/module/module.yaml b/tests/core/agent/module/module.yaml new file mode 100644 index 00000000..54c54a9b --- /dev/null +++ b/tests/core/agent/module/module.yaml @@ -0,0 +1,123 @@ +## DefaultTacticsAmbulanceTeam +DefaultTacticsAmbulanceTeam: + HumanDetector: sample_team.module.complex.SampleHumanDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionTransport: adf_core_python.implement.action.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorAmbulance: adf_core_python.implement.centralized.DefaultCommandExecutorAmbulance + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + +## DefaultTacticsFireBrigade +DefaultTacticsFireBrigade: + HumanDetector: sample_team.module.complex.SampleHumanDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionRescue: adf_core_python.implement.action.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorFire: adf_core_python.implement.centralized.DefaultCommandExecutorFire + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + +## DefaultTacticsPoliceForce +DefaultTacticsPoliceForce: + RoadDetector: sample_team.module.complex.SampleRoadDetector + Search: sample_team.module.complex.SampleSearch + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + CommandExecutorPolice: adf_core_python.implement.centralized.DefaultCommandExecutorPolice + CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScoutPolice + +## DefaultTacticsAmbulanceCentre +DefaultTacticsAmbulanceCentre: + TargetAllocator: sample_team.module.complex.SampleAmbulanceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerAmbulance + +## DefaultTacticsFireStation +DefaultTacticsFireStation: + TargetAllocator: sample_team.module.complex.SampleFireTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerFire + +## DefaultTacticsPoliceOffice +DefaultTacticsPoliceOffice: + TargetAllocator: sample_team.module.complex.SamplePoliceTargetAllocator + CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerPolice + +## SampleSearch +SampleSearch: + PathPlanning: + Ambulance: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Fire: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Police: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + Clustering: + Ambulance: adf_core_python.implement.module.algorithm.KMeansClustering + Fire: adf_core_python.implement.module.algorithm.KMeansClustering + Police: adf_core_python.implement.module.algorithm.KMeansClustering + +## SampleBuildDetector +SampleBuildingDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + +## SampleRoadDetector +SampleRoadDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## SampleHumanDetector +SampleHumanDetector: + Clustering: adf_core_python.implement.module.algorithm.KMeansClustering + +## DefaultExtendActionClear +DefaultExtendActionClear: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionFireFighting +DefaultExtendActionFireFighting: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionRescue +DefaultExtendActionRescue: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionMove +DefaultExtendActionMove: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultExtendActionTransport +DefaultExtendActionTransport: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultCommandExecutorAmbulance +DefaultCommandExecutorAmbulance: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionTransport: adf_core_python.implement.action.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorFire +DefaultCommandExecutorFire: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + EtxActionFireRescue: adf_core_python.implement.action.DefaultExtendActionRescue + EtxActionFireFighting: adf_core_python.implement.action.DefaultExtendActionFireFighting + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorPolice +DefaultCommandExecutorPolice: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove + +## DefaultCommandExecutorScout +DefaultCommandExecutorScout: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + +## DefaultCommandExecutorScoutPolice +DefaultCommandExecutorScoutPolice: + PathPlanning: adf_core_python.implement.module.algorithm.DijkstraPathPlanning + ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear + +## MessageManager +MessageManager: + PlatoonChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber + CenterChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber + PlatoonMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator + CenterMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator + +## VisualDebug +VisualDebug: true diff --git a/tests/core/agent/module/test_module_manager.py b/tests/core/agent/module/test_module_manager.py new file mode 100644 index 00000000..d6ec4980 --- /dev/null +++ b/tests/core/agent/module/test_module_manager.py @@ -0,0 +1,32 @@ +import os + +import pytest + +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.abstract_module import AbstractModule + + +class TestModuleManager: + @pytest.mark.skip(reason="一時的に無効化") + def test_can_get_module(self) -> None: + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_file_path = os.path.join(script_dir, "module.yaml") + config = ModuleConfig(config_file_path) + config.set_value( + "test_module", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ) + module_manager = self.create_module_manager(config) + module = module_manager.get_module("test_module", "test_module") + assert isinstance(module, AbstractModule) + assert module.__class__.__name__ == "AStarPathPlanning" + + def create_module_manager(self, config: ModuleConfig) -> ModuleManager: + return ModuleManager( + None, # type: ignore + None, # type: ignore + None, # type: ignore + config, + None, # type: ignore + ) diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..fba9de0f --- /dev/null +++ b/uv.lock @@ -0,0 +1,947 @@ +version = 1 +revision = 2 +requires-python = ">=3.13" + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, +] + +[[package]] +name = "adf-core-python" +version = "0.2.1" +source = { editable = "." } +dependencies = [ + { name = "bitarray" }, + { name = "click" }, + { name = "jinja2" }, + { name = "protobuf" }, + { name = "pyyaml" }, + { name = "rcrscore" }, + { name = "rtree" }, + { name = "scikit-learn" }, + { name = "shapely" }, + { name = "structlog" }, + { name = "types-pyyaml" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "myst-parser" }, + { name = "pytest" }, + { name = "ruff" }, + { name = "sphinx" }, + { name = "sphinx-book-theme" }, + { name = "sphinx-copybutton" }, + { name = "sphinx-intl" }, + { name = "sphinx-rtd-theme" }, + { name = "sphinx-togglebutton" }, + { name = "sphinxcontrib-mermaid" }, + { name = "types-protobuf" }, +] + +[package.metadata] +requires-dist = [ + { name = "bitarray", specifier = ">=3.6.0" }, + { name = "click", specifier = ">=8.2.1" }, + { name = "jinja2", specifier = ">=3.1.6" }, + { name = "protobuf", specifier = ">=6.31.1" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "rcrscore", git = "https://github.com/adf-python/rcrs-core-python?tag=v0.2.0" }, + { name = "rtree", specifier = ">=1.4.0" }, + { name = "scikit-learn", specifier = ">=1.7.1" }, + { name = "shapely", specifier = ">=2.1.1" }, + { name = "structlog", specifier = ">=25.4.0" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20250516" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.17.1" }, + { name = "myst-parser", specifier = ">=4.0.1" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "ruff", specifier = ">=0.12.5" }, + { name = "sphinx", specifier = ">=8.2.3" }, + { name = "sphinx-book-theme", specifier = ">=1.1.4" }, + { name = "sphinx-copybutton", specifier = ">=0.5.2" }, + { name = "sphinx-intl", specifier = ">=2.3.2" }, + { name = "sphinx-rtd-theme", specifier = ">=3.0.2" }, + { name = "sphinx-togglebutton", specifier = ">=0.3.2" }, + { name = "sphinxcontrib-mermaid", specifier = ">=1.0.0" }, + { name = "types-protobuf", specifier = ">=6.30.2.20250703" }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, +] + +[[package]] +name = "bitarray" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/ee/3b2fcbac3a4192e5d079aaa1850dff2f9ac625861c4c644819c2b34292ec/bitarray-3.6.0.tar.gz", hash = "sha256:20febc849a1f858e6a57a7d47b323fe9e727c579ddd526d317ad8831748a66a8", size = 147946, upload-time = "2025-07-29T18:03:56.681Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2c/21066c7a97b2c88037b0fc04480fa13b0031c30c6f70452dc9c84fb2b087/bitarray-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3f96f57cea35ba19fd23a20b38fa0dfa3d87d582507129b8c8e314aa298f59b", size = 144156, upload-time = "2025-07-29T18:01:35.58Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9cc42ea0c440ac1c2a65375688ac5891da12b3820f4a32440791d25ed668/bitarray-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81e84054b22babcd6c5cc1eac0de2bfc1054ecdf742720cbfb36efbe89ec6c30", size = 140916, upload-time = "2025-07-29T18:01:36.67Z" }, + { url = "https://files.pythonhosted.org/packages/d7/66/709d259d855528213b1099facddb08d6108cb0074cf88dc357cdd07bacff/bitarray-3.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca643295bf5441dd38dadf7571ca4b63961820eedbffbe46ceba0893bf226203", size = 324713, upload-time = "2025-07-29T18:01:37.925Z" }, + { url = "https://files.pythonhosted.org/packages/6c/67/831e366ea4f0d52d622482b8475f87040cbc210d8f5f383935a4cc6363fe/bitarray-3.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139963494fc3dd5caee5e38c0a03783ef50be118565e94b1dbb0210770f0b32d", size = 341300, upload-time = "2025-07-29T18:01:39.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/c9/197375b63ca768ac8b1e624f27dc0eccdd451f94c6b9bf8950500d8da134/bitarray-3.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:243825f56b58bef28bfc602992a8c6d09bbc625628c195498d6020120d632a09", size = 333724, upload-time = "2025-07-29T18:01:40.861Z" }, + { url = "https://files.pythonhosted.org/packages/e1/23/96c882d798b8bc9d5354ad1fba18ad3ad4f3c0a661a296c8e51ca2941e0f/bitarray-3.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:583b46b3ba44121de5e87e95ae379932dc5fd2e37ebdf2c11a6d7975891425c1", size = 327276, upload-time = "2025-07-29T18:01:42.039Z" }, + { url = "https://files.pythonhosted.org/packages/20/8e/51751fe0e6f9fe7980b0467b471ba9ab8d1713a2a6576980d18143511656/bitarray-3.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0be27d06732e2833b672a8fcc32fa195bdb22161eb88f8890de15e30264a01", size = 314903, upload-time = "2025-07-29T18:01:43.302Z" }, + { url = "https://files.pythonhosted.org/packages/49/7a/e4db9876e6e8bb261e64a384d3adb4372f13099b356e559cec85d022b897/bitarray-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:507e567aee4806576e20752f22533e8b7ec61e7e75062a7ce9222a0675aa0da6", size = 322551, upload-time = "2025-07-29T18:01:44.548Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/9460070e6cb671067cc2e115a82da6fc9ef0958542b98b07a5ed4a05a97b/bitarray-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22188943a29072b684cd7c99e0b2cfc0af317cea3366c583d820507e6d1f2ed4", size = 316128, upload-time = "2025-07-29T18:01:45.789Z" }, + { url = "https://files.pythonhosted.org/packages/34/6f/f5d78c8e908750b9c3d5839eca2af5f6e99d6c7fe8a0498ef79a1af90bd8/bitarray-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f92462ea3888c99439f58f7561ecd5dd4cf8b8b1b259ccf5376667b8c46ee747", size = 339337, upload-time = "2025-07-29T18:01:47.684Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d3/f740b601eae4e28e22d8560877fe9881f1b7a96fcb23b186e8580d328929/bitarray-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3800f3c8c9780f281cf590543fd4b3278fea6988202273a260ecc58136895efb", size = 338607, upload-time = "2025-07-29T18:01:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/4e/81/b9451089eea0ef66996852d2694b0f5afc0a76b1bc45c9a4f8204ae8674d/bitarray-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a50a66fa34dd7f9dcdbc7602a1b7bf6f9ab030b4f43e892324193423d9ede180", size = 324788, upload-time = "2025-07-29T18:01:51.454Z" }, + { url = "https://files.pythonhosted.org/packages/82/e8/80620fc60ad34bff647881a4f25c15b992c524e0f7af9c7c6c573b03556e/bitarray-3.6.0-cp313-cp313-win32.whl", hash = "sha256:afa24e5750c9b89ad5a7efef037efe49f4e339f20a94bf678c422c0c71e1207a", size = 137841, upload-time = "2025-07-29T18:01:52.95Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ee/303be88b847da29a067babc690e231d7838520dc1af57d14dad5a7ca095c/bitarray-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4c5e7edf1e7bcbde3b52058f171a411e2a24a081b3e951d685dfea4c3c383d5", size = 144820, upload-time = "2025-07-29T18:01:54.137Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475, upload-time = "2025-05-23T12:04:37.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542, upload-time = "2024-09-09T20:27:49.564Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316, upload-time = "2024-09-09T20:27:48.397Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mypy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "myst-parser" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/c0/c6bb172c916b00700ed3bf71cb56175fd1f7dbecebf8353545d0b5519f6c/numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3", size = 20949074, upload-time = "2025-07-24T20:43:07.813Z" }, + { url = "https://files.pythonhosted.org/packages/20/4e/c116466d22acaf4573e58421c956c6076dc526e24a6be0903219775d862e/numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b", size = 14177311, upload-time = "2025-07-24T20:43:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/78/45/d4698c182895af189c463fc91d70805d455a227261d950e4e0f1310c2550/numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6", size = 5106022, upload-time = "2025-07-24T20:43:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/9f/76/3e6880fef4420179309dba72a8c11f6166c431cf6dee54c577af8906f914/numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089", size = 6640135, upload-time = "2025-07-24T20:43:49.28Z" }, + { url = "https://files.pythonhosted.org/packages/34/fa/87ff7f25b3c4ce9085a62554460b7db686fef1e0207e8977795c7b7d7ba1/numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2", size = 14278147, upload-time = "2025-07-24T20:44:10.328Z" }, + { url = "https://files.pythonhosted.org/packages/1d/0f/571b2c7a3833ae419fe69ff7b479a78d313581785203cc70a8db90121b9a/numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f", size = 16635989, upload-time = "2025-07-24T20:44:34.88Z" }, + { url = "https://files.pythonhosted.org/packages/24/5a/84ae8dca9c9a4c592fe11340b36a86ffa9fd3e40513198daf8a97839345c/numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee", size = 16053052, upload-time = "2025-07-24T20:44:58.872Z" }, + { url = "https://files.pythonhosted.org/packages/57/7c/e5725d99a9133b9813fcf148d3f858df98511686e853169dbaf63aec6097/numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6", size = 18577955, upload-time = "2025-07-24T20:45:26.714Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/7c546fcf42145f29b71e4d6f429e96d8d68e5a7ba1830b2e68d7418f0bbd/numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b", size = 6311843, upload-time = "2025-07-24T20:49:24.444Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6f/a428fd1cb7ed39b4280d057720fed5121b0d7754fd2a9768640160f5517b/numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56", size = 12782876, upload-time = "2025-07-24T20:49:43.227Z" }, + { url = "https://files.pythonhosted.org/packages/65/85/4ea455c9040a12595fb6c43f2c217257c7b52dd0ba332c6a6c1d28b289fe/numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2", size = 10192786, upload-time = "2025-07-24T20:49:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/80/23/8278f40282d10c3f258ec3ff1b103d4994bcad78b0cba9208317f6bb73da/numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab", size = 21047395, upload-time = "2025-07-24T20:45:58.821Z" }, + { url = "https://files.pythonhosted.org/packages/1f/2d/624f2ce4a5df52628b4ccd16a4f9437b37c35f4f8a50d00e962aae6efd7a/numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2", size = 14300374, upload-time = "2025-07-24T20:46:20.207Z" }, + { url = "https://files.pythonhosted.org/packages/f6/62/ff1e512cdbb829b80a6bd08318a58698867bca0ca2499d101b4af063ee97/numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a", size = 5228864, upload-time = "2025-07-24T20:46:30.58Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8e/74bc18078fff03192d4032cfa99d5a5ca937807136d6f5790ce07ca53515/numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286", size = 6737533, upload-time = "2025-07-24T20:46:46.111Z" }, + { url = "https://files.pythonhosted.org/packages/19/ea/0731efe2c9073ccca5698ef6a8c3667c4cf4eea53fcdcd0b50140aba03bc/numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8", size = 14352007, upload-time = "2025-07-24T20:47:07.1Z" }, + { url = "https://files.pythonhosted.org/packages/cf/90/36be0865f16dfed20f4bc7f75235b963d5939707d4b591f086777412ff7b/numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a", size = 16701914, upload-time = "2025-07-24T20:47:32.459Z" }, + { url = "https://files.pythonhosted.org/packages/94/30/06cd055e24cb6c38e5989a9e747042b4e723535758e6153f11afea88c01b/numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91", size = 16132708, upload-time = "2025-07-24T20:47:58.129Z" }, + { url = "https://files.pythonhosted.org/packages/9a/14/ecede608ea73e58267fd7cb78f42341b3b37ba576e778a1a06baffbe585c/numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5", size = 18651678, upload-time = "2025-07-24T20:48:25.402Z" }, + { url = "https://files.pythonhosted.org/packages/40/f3/2fe6066b8d07c3685509bc24d56386534c008b462a488b7f503ba82b8923/numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5", size = 6441832, upload-time = "2025-07-24T20:48:37.181Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/0937d66d05204d8f28630c9c60bc3eda68824abde4cf756c4d6aad03b0c6/numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450", size = 12927049, upload-time = "2025-07-24T20:48:56.24Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ed/13542dd59c104d5e654dfa2ac282c199ba64846a74c2c4bcdbc3a0f75df1/numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a", size = 10262935, upload-time = "2025-07-24T20:49:13.136Z" }, + { url = "https://files.pythonhosted.org/packages/c9/7c/7659048aaf498f7611b783e000c7268fcc4dcf0ce21cd10aad7b2e8f9591/numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a", size = 20950906, upload-time = "2025-07-24T20:50:30.346Z" }, + { url = "https://files.pythonhosted.org/packages/80/db/984bea9d4ddf7112a04cfdfb22b1050af5757864cfffe8e09e44b7f11a10/numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b", size = 14185607, upload-time = "2025-07-24T20:50:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/b3d6f414f4eca568f469ac112a3b510938d892bc5a6c190cb883af080b77/numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125", size = 5114110, upload-time = "2025-07-24T20:51:01.041Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d2/6f5e6826abd6bca52392ed88fe44a4b52aacb60567ac3bc86c67834c3a56/numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19", size = 6642050, upload-time = "2025-07-24T20:51:11.64Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/f12b2ade99199e39c73ad182f103f9d9791f48d885c600c8e05927865baf/numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f", size = 14296292, upload-time = "2025-07-24T20:51:33.488Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f9/77c07d94bf110a916b17210fac38680ed8734c236bfed9982fd8524a7b47/numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5", size = 16638913, upload-time = "2025-07-24T20:51:58.517Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d1/9d9f2c8ea399cc05cfff8a7437453bd4e7d894373a93cdc46361bbb49a7d/numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58", size = 16071180, upload-time = "2025-07-24T20:52:22.827Z" }, + { url = "https://files.pythonhosted.org/packages/4c/41/82e2c68aff2a0c9bf315e47d61951099fed65d8cb2c8d9dc388cb87e947e/numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0", size = 18576809, upload-time = "2025-07-24T20:52:51.015Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/4b4fd3efb0837ed252d0f583c5c35a75121038a8c4e065f2c259be06d2d8/numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2", size = 6366410, upload-time = "2025-07-24T20:56:44.949Z" }, + { url = "https://files.pythonhosted.org/packages/11/9e/b4c24a6b8467b61aced5c8dc7dcfce23621baa2e17f661edb2444a418040/numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b", size = 12918821, upload-time = "2025-07-24T20:57:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0f/0dc44007c70b1007c1cef86b06986a3812dd7106d8f946c09cfa75782556/numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910", size = 10477303, upload-time = "2025-07-24T20:57:22.879Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3e/075752b79140b78ddfc9c0a1634d234cfdbc6f9bbbfa6b7504e445ad7d19/numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e", size = 21047524, upload-time = "2025-07-24T20:53:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6d/60e8247564a72426570d0e0ea1151b95ce5bd2f1597bb878a18d32aec855/numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45", size = 14300519, upload-time = "2025-07-24T20:53:44.053Z" }, + { url = "https://files.pythonhosted.org/packages/4d/73/d8326c442cd428d47a067070c3ac6cc3b651a6e53613a1668342a12d4479/numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b", size = 5228972, upload-time = "2025-07-24T20:53:53.81Z" }, + { url = "https://files.pythonhosted.org/packages/34/2e/e71b2d6dad075271e7079db776196829019b90ce3ece5c69639e4f6fdc44/numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2", size = 6737439, upload-time = "2025-07-24T20:54:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/15/b0/d004bcd56c2c5e0500ffc65385eb6d569ffd3363cb5e593ae742749b2daa/numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0", size = 14352479, upload-time = "2025-07-24T20:54:25.819Z" }, + { url = "https://files.pythonhosted.org/packages/11/e3/285142fcff8721e0c99b51686426165059874c150ea9ab898e12a492e291/numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0", size = 16702805, upload-time = "2025-07-24T20:54:50.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/33b56b0e47e604af2c7cd065edca892d180f5899599b76830652875249a3/numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2", size = 16133830, upload-time = "2025-07-24T20:55:17.306Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ae/7b1476a1f4d6a48bc669b8deb09939c56dd2a439db1ab03017844374fb67/numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf", size = 18652665, upload-time = "2025-07-24T20:55:46.665Z" }, + { url = "https://files.pythonhosted.org/packages/14/ba/5b5c9978c4bb161034148ade2de9db44ec316fab89ce8c400db0e0c81f86/numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1", size = 6514777, upload-time = "2025-07-24T20:55:57.66Z" }, + { url = "https://files.pythonhosted.org/packages/eb/46/3dbaf0ae7c17cdc46b9f662c56da2054887b8d9e737c1476f335c83d33db/numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b", size = 13111856, upload-time = "2025-07-24T20:56:17.318Z" }, + { url = "https://files.pythonhosted.org/packages/c1/9e/1652778bce745a67b5fe05adde60ed362d38eb17d919a540e813d30f6874/numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631", size = 10544226, upload-time = "2025-07-24T20:56:34.509Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, +] + +[[package]] +name = "pydata-sphinx-theme" +version = "0.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "babel" }, + { name = "beautifulsoup4" }, + { name = "docutils" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/d3/c622950d87a2ffd1654208733b5bd1c5645930014abed8f4c0d74863988b/pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6", size = 4640157, upload-time = "2024-06-25T19:28:42.383Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "rcrscore" +version = "0.1.0" +source = { git = "https://github.com/adf-python/rcrs-core-python?tag=v0.2.0#d8227cfcfb479c020fd0354a923aefcb6f2573df" } +dependencies = [ + { name = "protobuf" }, + { name = "rtree" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "roman-numerals-py" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, +] + +[[package]] +name = "rtree" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/b8/0091f020acafcb034daa5b062f0626f6a73c7e0d64826af23861390a9585/rtree-1.4.0.tar.gz", hash = "sha256:9d97c7c5dcf25f6c0599c76d9933368c6a8d7238f2c1d00e76f1a69369ca82a0", size = 50789, upload-time = "2025-03-05T23:31:45.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/4c/8d54d6dc5ff8ba8ced1fad9378f89f9dd60addcc4cf0e525ee0e67b1769f/rtree-1.4.0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:4d1bebc418101480aabf41767e772dd2155d3b27b1376cccbd93e4509485e091", size = 482755, upload-time = "2025-03-05T23:31:29.884Z" }, + { url = "https://files.pythonhosted.org/packages/20/29/045e700d2135e9a67896086c831fde80fd4105971b443d5727a4093fcbf1/rtree-1.4.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:997f8c38d5dffa3949ea8adb4c8b291ea5cd4ef5ee69455d642dd171baf9991d", size = 439796, upload-time = "2025-03-05T23:31:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fc/c3bd8cd67b10a12a6b9e2d06796779128c3e6968922dbf29fcd53af68d81/rtree-1.4.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0133d9c54ab3ffe874ba6d411dbe0254765c5e68d92da5b91362c370f16fd997", size = 497549, upload-time = "2025-03-05T23:31:33.722Z" }, + { url = "https://files.pythonhosted.org/packages/a0/dd/49dc9ab037d0cb288ed40f8b7f498f69d44243e4745e241c05d5e457ea8b/rtree-1.4.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:d3b7bf1fe6463139377995ebe22a01a7005d134707f43672a3c09305e12f5f43", size = 568787, upload-time = "2025-03-05T23:31:35.478Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e7/57737dff73ce789bdadd916d48ac12e977d8578176e1e890b1b8d89b9dbf/rtree-1.4.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:27e4a6d617d63dcb82fcd4c2856134b8a3741bd1af3b1a0d98e886054f394da5", size = 541090, upload-time = "2025-03-05T23:31:37.712Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8f/1f3f716c4e8388670cfd5d0a3578e2354a1e6a3403648e234e1540e3e3bd/rtree-1.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5258e826064eab82439760201e9421ce6d4340789d6d080c1b49367ddd03f61f", size = 1454194, upload-time = "2025-03-05T23:31:39.851Z" }, + { url = "https://files.pythonhosted.org/packages/22/ec/b42052b10e63a1c5d5d61ce234332f689736053644ba1756f7a632ea7659/rtree-1.4.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:20d5b3f9cf8bbbcc9fec42ab837c603c5dd86103ef29134300c8da2495c1248b", size = 1692814, upload-time = "2025-03-05T23:31:41.617Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5b/a9920e9a2dc43b066ff13b7fde2e7bffcca315cfa43ae6f4cc15970e39eb/rtree-1.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a67bee1233370a4c72c0969a96d2a1df1ba404ddd9f146849c53ab420eab361b", size = 1554860, upload-time = "2025-03-05T23:31:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c2/362f2cc36a7a57b47380061c23fc109c7222c1a544ffd24cda289ba19673/rtree-1.4.0-py3-none-win_amd64.whl", hash = "sha256:ba83efc7b7563905b1bfdfc14490c4bfb59e92e5e6156bdeb6ec5df5117252f4", size = 385221, upload-time = "2025-03-05T23:31:44.537Z" }, +] + +[[package]] +name = "ruff" +version = "0.12.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/81/0bd3594fa0f690466e41bd033bdcdf86cba8288345ac77ad4afbe5ec743a/ruff-0.12.7.tar.gz", hash = "sha256:1fc3193f238bc2d7968772c82831a4ff69252f673be371fb49663f0068b7ec71", size = 5197814, upload-time = "2025-07-29T22:32:35.877Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/d2/6cb35e9c85e7a91e8d22ab32ae07ac39cc34a71f1009a6f9e4a2a019e602/ruff-0.12.7-py3-none-linux_armv6l.whl", hash = "sha256:76e4f31529899b8c434c3c1dede98c4483b89590e15fb49f2d46183801565303", size = 11852189, upload-time = "2025-07-29T22:31:41.281Z" }, + { url = "https://files.pythonhosted.org/packages/63/5b/a4136b9921aa84638f1a6be7fb086f8cad0fde538ba76bda3682f2599a2f/ruff-0.12.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:789b7a03e72507c54fb3ba6209e4bb36517b90f1a3569ea17084e3fd295500fb", size = 12519389, upload-time = "2025-07-29T22:31:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c9/3e24a8472484269b6b1821794141f879c54645a111ded4b6f58f9ab0705f/ruff-0.12.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e1c2a3b8626339bb6369116e7030a4cf194ea48f49b64bb505732a7fce4f4e3", size = 11743384, upload-time = "2025-07-29T22:31:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/26/7c/458dd25deeb3452c43eaee853c0b17a1e84169f8021a26d500ead77964fd/ruff-0.12.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32dec41817623d388e645612ec70d5757a6d9c035f3744a52c7b195a57e03860", size = 11943759, upload-time = "2025-07-29T22:32:01.95Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8b/658798472ef260ca050e400ab96ef7e85c366c39cf3dfbef4d0a46a528b6/ruff-0.12.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47ef751f722053a5df5fa48d412dbb54d41ab9b17875c6840a58ec63ff0c247c", size = 11654028, upload-time = "2025-07-29T22:32:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/a8/86/9c2336f13b2a3326d06d39178fd3448dcc7025f82514d1b15816fe42bfe8/ruff-0.12.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a828a5fc25a3efd3e1ff7b241fd392686c9386f20e5ac90aa9234a5faa12c423", size = 13225209, upload-time = "2025-07-29T22:32:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/76/69/df73f65f53d6c463b19b6b312fd2391dc36425d926ec237a7ed028a90fc1/ruff-0.12.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5726f59b171111fa6a69d82aef48f00b56598b03a22f0f4170664ff4d8298efb", size = 14182353, upload-time = "2025-07-29T22:32:10.053Z" }, + { url = "https://files.pythonhosted.org/packages/58/1e/de6cda406d99fea84b66811c189b5ea139814b98125b052424b55d28a41c/ruff-0.12.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74e6f5c04c4dd4aba223f4fe6e7104f79e0eebf7d307e4f9b18c18362124bccd", size = 13631555, upload-time = "2025-07-29T22:32:12.644Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ae/625d46d5164a6cc9261945a5e89df24457dc8262539ace3ac36c40f0b51e/ruff-0.12.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0bfe4e77fba61bf2ccadf8cf005d6133e3ce08793bbe870dd1c734f2699a3e", size = 12667556, upload-time = "2025-07-29T22:32:15.312Z" }, + { url = "https://files.pythonhosted.org/packages/55/bf/9cb1ea5e3066779e42ade8d0cd3d3b0582a5720a814ae1586f85014656b6/ruff-0.12.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06bfb01e1623bf7f59ea749a841da56f8f653d641bfd046edee32ede7ff6c606", size = 12939784, upload-time = "2025-07-29T22:32:17.69Z" }, + { url = "https://files.pythonhosted.org/packages/55/7f/7ead2663be5627c04be83754c4f3096603bf5e99ed856c7cd29618c691bd/ruff-0.12.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e41df94a957d50083fd09b916d6e89e497246698c3f3d5c681c8b3e7b9bb4ac8", size = 11771356, upload-time = "2025-07-29T22:32:20.134Z" }, + { url = "https://files.pythonhosted.org/packages/17/40/a95352ea16edf78cd3a938085dccc55df692a4d8ba1b3af7accbe2c806b0/ruff-0.12.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4000623300563c709458d0ce170c3d0d788c23a058912f28bbadc6f905d67afa", size = 11612124, upload-time = "2025-07-29T22:32:22.645Z" }, + { url = "https://files.pythonhosted.org/packages/4d/74/633b04871c669e23b8917877e812376827c06df866e1677f15abfadc95cb/ruff-0.12.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:69ffe0e5f9b2cf2b8e289a3f8945b402a1b19eff24ec389f45f23c42a3dd6fb5", size = 12479945, upload-time = "2025-07-29T22:32:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/be/34/c3ef2d7799c9778b835a76189c6f53c179d3bdebc8c65288c29032e03613/ruff-0.12.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a07a5c8ffa2611a52732bdc67bf88e243abd84fe2d7f6daef3826b59abbfeda4", size = 12998677, upload-time = "2025-07-29T22:32:27.022Z" }, + { url = "https://files.pythonhosted.org/packages/77/ab/aca2e756ad7b09b3d662a41773f3edcbd262872a4fc81f920dc1ffa44541/ruff-0.12.7-py3-none-win32.whl", hash = "sha256:c928f1b2ec59fb77dfdf70e0419408898b63998789cc98197e15f560b9e77f77", size = 11756687, upload-time = "2025-07-29T22:32:29.381Z" }, + { url = "https://files.pythonhosted.org/packages/b4/71/26d45a5042bc71db22ddd8252ca9d01e9ca454f230e2996bb04f16d72799/ruff-0.12.7-py3-none-win_amd64.whl", hash = "sha256:9c18f3d707ee9edf89da76131956aba1270c6348bfee8f6c647de841eac7194f", size = 12912365, upload-time = "2025-07-29T22:32:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/4c/9b/0b8aa09817b63e78d94b4977f18b1fcaead3165a5ee49251c5d5c245bb2d/ruff-0.12.7-py3-none-win_arm64.whl", hash = "sha256:dfce05101dbd11833a0776716d5d1578641b7fddb537fe7fa956ab85d1769b69", size = 11982083, upload-time = "2025-07-29T22:32:33.881Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/84/5f4af978fff619706b8961accac84780a6d298d82a8873446f72edb4ead0/scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802", size = 7190445, upload-time = "2025-07-18T08:01:54.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/f8/e0533303f318a0f37b88300d21f79b6ac067188d4824f1047a37214ab718/scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae", size = 9213143, upload-time = "2025-07-18T08:01:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/71/f3/f1df377d1bdfc3e3e2adc9c119c238b182293e6740df4cbeac6de2cc3e23/scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10", size = 8591977, upload-time = "2025-07-18T08:01:34.967Z" }, + { url = "https://files.pythonhosted.org/packages/99/72/c86a4cd867816350fe8dee13f30222340b9cd6b96173955819a5561810c5/scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309", size = 9436142, upload-time = "2025-07-18T08:01:37.397Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/277967b29bd297538dc7a6ecfb1a7dce751beabd0d7f7a2233be7a4f7832/scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43", size = 9282996, upload-time = "2025-07-18T08:01:39.721Z" }, + { url = "https://files.pythonhosted.org/packages/e2/47/9291cfa1db1dae9880420d1e07dbc7e8dd4a7cdbc42eaba22512e6bde958/scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11", size = 8707418, upload-time = "2025-07-18T08:01:42.124Z" }, + { url = "https://files.pythonhosted.org/packages/61/95/45726819beccdaa34d3362ea9b2ff9f2b5d3b8bf721bd632675870308ceb/scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae", size = 9561466, upload-time = "2025-07-18T08:01:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1c/6f4b3344805de783d20a51eb24d4c9ad4b11a7f75c1801e6ec6d777361fd/scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c", size = 9040467, upload-time = "2025-07-18T08:01:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/6f/80/abe18fe471af9f1d181904203d62697998b27d9b62124cd281d740ded2f9/scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e", size = 9532052, upload-time = "2025-07-18T08:01:48.676Z" }, + { url = "https://files.pythonhosted.org/packages/14/82/b21aa1e0c4cee7e74864d3a5a721ab8fcae5ca55033cb6263dca297ed35b/scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7", size = 9361575, upload-time = "2025-07-18T08:01:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/f4777fcd5627dc6695fa6b92179d0edb7a3ac1b91bcd9a1c7f64fa7ade23/scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5", size = 9277310, upload-time = "2025-07-18T08:01:52.547Z" }, +] + +[[package]] +name = "scipy" +version = "1.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/4a/b927028464795439faec8eaf0b03b011005c487bb2d07409f28bf30879c4/scipy-1.16.1.tar.gz", hash = "sha256:44c76f9e8b6e8e488a586190ab38016e4ed2f8a038af7cd3defa903c0a2238b3", size = 30580861, upload-time = "2025-07-27T16:33:30.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5451606823a5e73dfa621a89948096c6528e2896e40b39248295d3a0138d594f", size = 36533717, upload-time = "2025-07-27T16:28:51.706Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:89728678c5ca5abd610aee148c199ac1afb16e19844401ca97d43dc548a354eb", size = 28570009, upload-time = "2025-07-27T16:28:57.017Z" }, + { url = "https://files.pythonhosted.org/packages/f4/69/bd75dbfdd3cf524f4d753484d723594aed62cfaac510123e91a6686d520b/scipy-1.16.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e756d688cb03fd07de0fffad475649b03cb89bee696c98ce508b17c11a03f95c", size = 20841942, upload-time = "2025-07-27T16:29:01.152Z" }, + { url = "https://files.pythonhosted.org/packages/ea/74/add181c87663f178ba7d6144b370243a87af8476664d5435e57d599e6874/scipy-1.16.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5aa2687b9935da3ed89c5dbed5234576589dd28d0bf7cd237501ccfbdf1ad608", size = 23498507, upload-time = "2025-07-27T16:29:05.202Z" }, + { url = "https://files.pythonhosted.org/packages/1d/74/ece2e582a0d9550cee33e2e416cc96737dce423a994d12bbe59716f47ff1/scipy-1.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0851f6a1e537fe9399f35986897e395a1aa61c574b178c0d456be5b1a0f5ca1f", size = 33286040, upload-time = "2025-07-27T16:29:10.201Z" }, + { url = "https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fedc2cbd1baed37474b1924c331b97bdff611d762c196fac1a9b71e67b813b1b", size = 35176096, upload-time = "2025-07-27T16:29:17.091Z" }, + { url = "https://files.pythonhosted.org/packages/fa/79/cd710aab8c921375711a8321c6be696e705a120e3011a643efbbcdeeabcc/scipy-1.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2ef500e72f9623a6735769e4b93e9dcb158d40752cdbb077f305487e3e2d1f45", size = 35490328, upload-time = "2025-07-27T16:29:22.928Z" }, + { url = "https://files.pythonhosted.org/packages/71/73/e9cc3d35ee4526d784520d4494a3e1ca969b071fb5ae5910c036a375ceec/scipy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:978d8311674b05a8f7ff2ea6c6bce5d8b45a0cb09d4c5793e0318f448613ea65", size = 37939921, upload-time = "2025-07-27T16:29:29.108Z" }, + { url = "https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:81929ed0fa7a5713fcdd8b2e6f73697d3b4c4816d090dd34ff937c20fa90e8ab", size = 38479462, upload-time = "2025-07-27T16:30:24.078Z" }, + { url = "https://files.pythonhosted.org/packages/7a/19/c3d08b675260046a991040e1ea5d65f91f40c7df1045fffff412dcfc6765/scipy-1.16.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:bcc12db731858abda693cecdb3bdc9e6d4bd200213f49d224fe22df82687bdd6", size = 36938832, upload-time = "2025-07-27T16:29:35.057Z" }, + { url = "https://files.pythonhosted.org/packages/81/f2/ce53db652c033a414a5b34598dba6b95f3d38153a2417c5a3883da429029/scipy-1.16.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:744d977daa4becb9fc59135e75c069f8d301a87d64f88f1e602a9ecf51e77b27", size = 29093084, upload-time = "2025-07-27T16:29:40.201Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/7a10ff04a7dc15f9057d05b33737ade244e4bd195caa3f7cc04d77b9e214/scipy-1.16.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:dc54f76ac18073bcecffb98d93f03ed6b81a92ef91b5d3b135dcc81d55a724c7", size = 21365098, upload-time = "2025-07-27T16:29:44.295Z" }, + { url = "https://files.pythonhosted.org/packages/36/ac/029ff710959932ad3c2a98721b20b405f05f752f07344622fd61a47c5197/scipy-1.16.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:367d567ee9fc1e9e2047d31f39d9d6a7a04e0710c86e701e053f237d14a9b4f6", size = 23896858, upload-time = "2025-07-27T16:29:48.784Z" }, + { url = "https://files.pythonhosted.org/packages/71/13/d1ef77b6bd7898720e1f0b6b3743cb945f6c3cafa7718eaac8841035ab60/scipy-1.16.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cf5785e44e19dcd32a0e4807555e1e9a9b8d475c6afff3d21c3c543a6aa84f4", size = 33438311, upload-time = "2025-07-27T16:29:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e0/e64a6821ffbb00b4c5b05169f1c1fddb4800e9307efe3db3788995a82a2c/scipy-1.16.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3d0b80fb26d3e13a794c71d4b837e2a589d839fd574a6bbb4ee1288c213ad4a3", size = 35279542, upload-time = "2025-07-27T16:30:00.249Z" }, + { url = "https://files.pythonhosted.org/packages/57/59/0dc3c8b43e118f1e4ee2b798dcc96ac21bb20014e5f1f7a8e85cc0653bdb/scipy-1.16.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8503517c44c18d1030d666cb70aaac1cc8913608816e06742498833b128488b7", size = 35667665, upload-time = "2025-07-27T16:30:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/45/5f/844ee26e34e2f3f9f8febb9343748e72daeaec64fe0c70e9bf1ff84ec955/scipy-1.16.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:30cc4bb81c41831ecfd6dc450baf48ffd80ef5aed0f5cf3ea775740e80f16ecc", size = 38045210, upload-time = "2025-07-27T16:30:11.655Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d7/210f2b45290f444f1de64bc7353aa598ece9f0e90c384b4a156f9b1a5063/scipy-1.16.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c24fa02f7ed23ae514460a22c57eca8f530dbfa50b1cfdbf4f37c05b5309cc39", size = 38593661, upload-time = "2025-07-27T16:30:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/81/ea/84d481a5237ed223bd3d32d6e82d7a6a96e34756492666c260cef16011d1/scipy-1.16.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:796a5a9ad36fa3a782375db8f4241ab02a091308eb079746bc0f874c9b998318", size = 36525921, upload-time = "2025-07-27T16:30:30.081Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9f/d9edbdeff9f3a664807ae3aea383e10afaa247e8e6255e6d2aa4515e8863/scipy-1.16.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:3ea0733a2ff73fd6fdc5fecca54ee9b459f4d74f00b99aced7d9a3adb43fb1cc", size = 28564152, upload-time = "2025-07-27T16:30:35.336Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/8125bcb1fe04bc267d103e76516243e8d5e11229e6b306bda1024a5423d1/scipy-1.16.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:85764fb15a2ad994e708258bb4ed8290d1305c62a4e1ef07c414356a24fcfbf8", size = 20836028, upload-time = "2025-07-27T16:30:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/77/9c/bf92e215701fc70bbcd3d14d86337cf56a9b912a804b9c776a269524a9e9/scipy-1.16.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ca66d980469cb623b1759bdd6e9fd97d4e33a9fad5b33771ced24d0cb24df67e", size = 23489666, upload-time = "2025-07-27T16:30:43.663Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/5e941d397d9adac41b02839011594620d54d99488d1be5be755c00cde9ee/scipy-1.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7cc1ffcc230f568549fc56670bcf3df1884c30bd652c5da8138199c8c76dae0", size = 33358318, upload-time = "2025-07-27T16:30:48.982Z" }, + { url = "https://files.pythonhosted.org/packages/0e/87/8db3aa10dde6e3e8e7eb0133f24baa011377d543f5b19c71469cf2648026/scipy-1.16.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ddfb1e8d0b540cb4ee9c53fc3dea3186f97711248fb94b4142a1b27178d8b4b", size = 35185724, upload-time = "2025-07-27T16:30:54.26Z" }, + { url = "https://files.pythonhosted.org/packages/89/b4/6ab9ae443216807622bcff02690262d8184078ea467efee2f8c93288a3b1/scipy-1.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4dc0e7be79e95d8ba3435d193e0d8ce372f47f774cffd882f88ea4e1e1ddc731", size = 35554335, upload-time = "2025-07-27T16:30:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9a/d0e9dc03c5269a1afb60661118296a32ed5d2c24298af61b676c11e05e56/scipy-1.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f23634f9e5adb51b2a77766dac217063e764337fbc816aa8ad9aaebcd4397fd3", size = 37960310, upload-time = "2025-07-27T16:31:06.151Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/c8f3130a50521a7977874817ca89e0599b1b4ee8e938bad8ae798a0e1f0d/scipy-1.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:57d75524cb1c5a374958a2eae3d84e1929bb971204cc9d52213fb8589183fc19", size = 39319239, upload-time = "2025-07-27T16:31:59.942Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f2/1ca3eda54c3a7e4c92f6acef7db7b3a057deb135540d23aa6343ef8ad333/scipy-1.16.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:d8da7c3dd67bcd93f15618938f43ed0995982eb38973023d46d4646c4283ad65", size = 36939460, upload-time = "2025-07-27T16:31:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/80/30/98c2840b293a132400c0940bb9e140171dcb8189588619048f42b2ce7b4f/scipy-1.16.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:cc1d2f2fd48ba1e0620554fe5bc44d3e8f5d4185c8c109c7fbdf5af2792cfad2", size = 29093322, upload-time = "2025-07-27T16:31:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e6/1e6e006e850622cf2a039b62d1a6ddc4497d4851e58b68008526f04a9a00/scipy-1.16.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:21a611ced9275cb861bacadbada0b8c0623bc00b05b09eb97f23b370fc2ae56d", size = 21365329, upload-time = "2025-07-27T16:31:21.188Z" }, + { url = "https://files.pythonhosted.org/packages/8e/02/72a5aa5b820589dda9a25e329ca752842bfbbaf635e36bc7065a9b42216e/scipy-1.16.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dfbb25dffc4c3dd9371d8ab456ca81beeaf6f9e1c2119f179392f0dc1ab7695", size = 23897544, upload-time = "2025-07-27T16:31:25.408Z" }, + { url = "https://files.pythonhosted.org/packages/2b/dc/7122d806a6f9eb8a33532982234bed91f90272e990f414f2830cfe656e0b/scipy-1.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0ebb7204f063fad87fc0a0e4ff4a2ff40b2a226e4ba1b7e34bf4b79bf97cd86", size = 33442112, upload-time = "2025-07-27T16:31:30.62Z" }, + { url = "https://files.pythonhosted.org/packages/24/39/e383af23564daa1021a5b3afbe0d8d6a68ec639b943661841f44ac92de85/scipy-1.16.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1b9e5962656f2734c2b285a8745358ecb4e4efbadd00208c80a389227ec61ff", size = 35286594, upload-time = "2025-07-27T16:31:36.112Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/1a0b0aff40c3056d955f38b0df5d178350c3d74734ec54f9c68d23910be5/scipy-1.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e1a106f8c023d57a2a903e771228bf5c5b27b5d692088f457acacd3b54511e4", size = 35665080, upload-time = "2025-07-27T16:31:42.025Z" }, + { url = "https://files.pythonhosted.org/packages/64/df/ce88803e9ed6e27fe9b9abefa157cf2c80e4fa527cf17ee14be41f790ad4/scipy-1.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:709559a1db68a9abc3b2c8672c4badf1614f3b440b3ab326d86a5c0491eafae3", size = 38050306, upload-time = "2025-07-27T16:31:48.109Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6c/a76329897a7cae4937d403e623aa6aaea616a0bb5b36588f0b9d1c9a3739/scipy-1.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c0c804d60492a0aad7f5b2bb1862f4548b990049e27e828391ff2bf6f7199998", size = 39427705, upload-time = "2025-07-27T16:31:53.96Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "shapely" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, + { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, + { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, + { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, + { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, + { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, + { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, + { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, +] + +[[package]] +name = "sphinx" +version = "8.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals-py" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, +] + +[[package]] +name = "sphinx-book-theme" +version = "1.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/9e/c41d68be04eef5b6202b468e0f90faf0c469f3a03353f2a218fd78279710/sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1", size = 433952, upload-time = "2025-02-20T16:32:31.009Z" }, +] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, +] + +[[package]] +name = "sphinx-intl" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "click" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/21/eb12016ecb0b52861762b0d227dff75622988f238776a5ee4c75bade507e/sphinx_intl-2.3.2.tar.gz", hash = "sha256:04b0d8ea04d111a7ba278b17b7b3fe9625c58b6f8ffb78bb8a1dd1288d88c1c7", size = 27921, upload-time = "2025-08-02T04:53:01.891Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3b/156032fa29a87e9eba9182b8e893a7e88c1d98907a078a371d69be432e52/sphinx_intl-2.3.2-py3-none-any.whl", hash = "sha256:f0082f9383066bab8406129a2ed531d21c38706d08467bf5ca3714e8914bb2be", size = 12899, upload-time = "2025-08-02T04:53:00.353Z" }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561, upload-time = "2024-11-13T11:06:02.094Z" }, +] + +[[package]] +name = "sphinx-togglebutton" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "setuptools" }, + { name = "sphinx" }, + { name = "wheel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/df/d151dfbbe588116e450ca7e898750cb218dca6b2e557ced8de6f9bd7242b/sphinx-togglebutton-0.3.2.tar.gz", hash = "sha256:ab0c8b366427b01e4c89802d5d078472c427fa6e9d12d521c34fa0442559dc7a", size = 8324, upload-time = "2022-07-15T12:08:50.286Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/18/267ce39f29d26cdc7177231428ba823fe5ca94db8c56d1bed69033b364c8/sphinx_togglebutton-0.3.2-py3-none-any.whl", hash = "sha256:9647ba7874b7d1e2d43413d8497153a85edc6ac95a3fea9a75ef9c1e08aaae2b", size = 8249, upload-time = "2022-07-15T12:08:48.8Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-mermaid" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/69/bf039237ad260073e8c02f820b3e00dc34f3a2de20aff7861e6b19d2f8c5/sphinxcontrib_mermaid-1.0.0.tar.gz", hash = "sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146", size = 15153, upload-time = "2024-10-12T16:33:03.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/c8/784b9ac6ea08aa594c1a4becbd0dbe77186785362e31fd633b8c6ae0197a/sphinxcontrib_mermaid-1.0.0-py3-none-any.whl", hash = "sha256:60b72710ea02087f212028feb09711225fbc2e343a10d34822fe787510e1caa3", size = 9597, upload-time = "2024-10-12T16:33:02.303Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "structlog" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.30.2.20250703" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/54/d63ce1eee8e93c4d710bbe2c663ec68e3672cf4f2fca26eecd20981c0c5d/types_protobuf-6.30.2.20250703.tar.gz", hash = "sha256:609a974754bbb71fa178fc641f51050395e8e1849f49d0420a6281ed8d1ddf46", size = 62300, upload-time = "2025-07-03T03:14:05.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/2b/5d0377c3d6e0f49d4847ad2c40629593fee4a5c9ec56eba26a15c708fbc0/types_protobuf-6.30.2.20250703-py3-none-any.whl", hash = "sha256:fa5aff9036e9ef432d703abbdd801b436a249b6802e4df5ef74513e272434e57", size = 76489, upload-time = "2025-07-03T03:14:04.453Z" }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "wheel" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, +]