diff --git a/.github/workflows/docs-cd.yml b/.github/workflows/docs-cd.yml new file mode 100644 index 0000000..f78e8ce --- /dev/null +++ b/.github/workflows/docs-cd.yml @@ -0,0 +1,57 @@ +name: 'Documentation: Publish' + +on: + push: + branches: ['dev'] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install -r docs/requirements.txt + + - name: Generate API stubs + run: | + sphinx-apidoc -o docs/api src/robot + + - name: Build HTML + run: | + python -m sphinx -b html docs docs/_build/html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/_build/html + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index bbae27b..0be23ab 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,115 @@ -# Robot Library Python +# Robot Library (Python) -A modular Python library for building robot applications. It provides components -for motion control, sensor access, communication, and indicators. +A modular Python library for building robot applications. It provides components for motion control, sensors, communications, and indicators over MQTT. + +Documentation: the full site is auto-built and deployed to GitHub Pages on pushes to the `dev` branch. Once enabled in the repo settings, it will be available at: `https://pera-swarm.github.io/robot-library-python/`. ## Installation -```bash -pip install -e . +You can install in a few ways: + +- Editable (development) install from a local checkout: + + ```bash + pip install -e . + ``` + +- From source via Git (replace org/repo as appropriate): + +This library requires `paho-mqtt` (declared in `pyproject.toml`) and Python 3.8+. + +## Quick Start + +Create a virtual robot by subclassing `robot.VirtualRobot`, configure MQTT, and start the main loop: + +```python +from robot import MQTTSettings, VirtualRobot +from robot.interfaces import RobotState + + +class MyRobot(VirtualRobot): + def loop(self): + if self.state == RobotState.RUN: + print("running...") + # do work + self.delay(1000) + + +# Configure MQTT broker connection +MQTTSettings.server = "localhost" +MQTTSettings.port = 1883 +MQTTSettings.user_name = "" +MQTTSettings.password = "" +MQTTSettings.channel = "v1" + +r = MyRobot(1, 0, 0, 90) +r.start() +r.run() # or run in a separate thread +``` + +See `examples/my_test_robot.py` for a complete runnable example. + +## Core Concepts + +- Robot: base lifecycle (`start/stop/reset/delay/loop`) and MQTT subscription handling. See `robot.Robot`. +- VirtualRobot: convenient virtual implementation; subclass and implement `loop`. See `robot.VirtualRobot`. +- Motion: differential drive style controller for movement and rotation. See `robot.MotionController`. +- Sensors: distance, color, and proximity, with non-blocking MQTT updates and blocking request/reply helpers. +- Communication: simple and directed messaging channels to other robots or a controller. +- Indicators: NeoPixel control via MQTT topics. + +## Usage Examples + +Motion control: + +```python +from robot import MotionController + +motion = MotionController() # standalone stub coordinate for quick tests +motion.move(100, 100, duration=1000) # forward 1s +motion.rotate_degree(120, 90) # rotate 90 degrees at speed 120 +``` + +Sensors (within a `Robot` subclass after `setup()` has run): + +```python +dist = self.dist_sensor.get_distance() +rgb = self.color_sensor.get_color() # RGBColorType +prox = self.proximity_sensor.get_proximity() # ProximityReadingType +``` + +Communications: + +```python +self.simple_comm.send_message("hello swarm") +self.directed_comm.send_message_with_distance("to leader", distance=25) ``` -## Usage +Indicators: ```python -from robot.motion import MotionController +self.neo_pixel.change_color(255, 128, 0) +``` + +## Extending and Customizing + +- New behavior: subclass `VirtualRobot` and implement `loop`. Optionally override `sensor_interrupt` or `communication_interrupt`. +- New sensors/indicators: follow the pattern in `robot.sensors.*` and `robot.indicators.*`, implementing `handle_subscription` and publishing/consuming your desired topics via `RobotMqttClient`. +- Motion tuning: adjust `robot.configs.robot_settings.RobotSettings` constants (speed bounds, robot geometry) to match your platform. +- MQTT setup: set fields on `robot.configs.mqtt_settings.MQTTSettings` before constructing robots. -controller = MotionController() +## Documentation + +Sphinx documentation lives in `docs/` and is auto-generated on pushes to `dev` via GitHub Actions, then deployed to GitHub Pages. To build locally: + +```bash +pip install -r docs/requirements.txt +sphinx-apidoc -o docs/api src/robot +python -m sphinx -b html docs docs/_build/html +open docs/_build/html/index.html ``` + +## Contributing + +- Run lint and tests: `flake8 src tests` and `PYTHONPATH=src pytest` +- Example to run: `python examples/my_test_robot.py` diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b4f7145 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,29 @@ +# Minimal Sphinx Makefile + +SPHINXBUILD?=sphinx-build +SOURCEDIR=. +BUILDDIR=_build +ROOTDIR=.. +PY?=python +PORT?= 9000 + +.PHONY: help clean html apidoc + +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" + +clean: + rm -rf "$(BUILDDIR)" + +apidoc: + @if command -v sphinx-apidoc >/dev/null 2>&1; then \ + sphinx-apidoc -o api $(ROOTDIR)/src/robot; \ + else \ + $(PY) -m sphinx.ext.apidoc -o api $(ROOTDIR)/src/robot; \ + fi + +html: + $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" + +livehtml: + sphinx-autobuild --host 0.0.0.0 --port ${PORT} -c . "$(SOURCEDIR)" "$(BUILDDIR)/html" diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/api/modules.rst b/docs/api/modules.rst new file mode 100644 index 0000000..9c1b3af --- /dev/null +++ b/docs/api/modules.rst @@ -0,0 +1,7 @@ +robot +===== + +.. toctree:: + :maxdepth: 4 + + robot diff --git a/docs/api/robot.communication.rst b/docs/api/robot.communication.rst new file mode 100644 index 0000000..e53e76d --- /dev/null +++ b/docs/api/robot.communication.rst @@ -0,0 +1,29 @@ +robot.communication package +=========================== + +Submodules +---------- + +robot.communication.directed\_comm module +----------------------------------------- + +.. automodule:: robot.communication.directed_comm + :members: + :show-inheritance: + :undoc-members: + +robot.communication.simple\_comm module +--------------------------------------- + +.. automodule:: robot.communication.simple_comm + :members: + :show-inheritance: + :undoc-members: + +Module contents +--------------- + +.. automodule:: robot.communication + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/api/robot.helpers.rst b/docs/api/robot.helpers.rst new file mode 100644 index 0000000..b84e488 --- /dev/null +++ b/docs/api/robot.helpers.rst @@ -0,0 +1,29 @@ +robot.helpers package +===================== + +Submodules +---------- + +robot.helpers.coordinate module +------------------------------- + +.. automodule:: robot.helpers.coordinate + :members: + :show-inheritance: + :undoc-members: + +robot.helpers.robot\_mqtt module +-------------------------------- + +.. automodule:: robot.helpers.robot_mqtt + :members: + :show-inheritance: + :undoc-members: + +Module contents +--------------- + +.. automodule:: robot.helpers + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/api/robot.indicators.rst b/docs/api/robot.indicators.rst new file mode 100644 index 0000000..3b831c6 --- /dev/null +++ b/docs/api/robot.indicators.rst @@ -0,0 +1,21 @@ +robot.indicators package +======================== + +Submodules +---------- + +robot.indicators.neopixel module +-------------------------------- + +.. automodule:: robot.indicators.neopixel + :members: + :show-inheritance: + :undoc-members: + +Module contents +--------------- + +.. automodule:: robot.indicators + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/api/robot.rst b/docs/api/robot.rst new file mode 100644 index 0000000..547e914 --- /dev/null +++ b/docs/api/robot.rst @@ -0,0 +1,48 @@ +robot package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + robot.communication + robot.helpers + robot.indicators + robot.sensors + +Submodules +---------- + +robot.motion module +------------------- + +.. automodule:: robot.motion + :members: + :show-inheritance: + :undoc-members: + +robot.mqtt\_client module +------------------------- + +.. automodule:: robot.mqtt_client + :members: + :show-inheritance: + :undoc-members: + +robot.robot\_base module +------------------------ + +.. automodule:: robot.robot_base + :members: + :show-inheritance: + :undoc-members: + +Module contents +--------------- + +.. automodule:: robot + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/api/robot.sensors.rst b/docs/api/robot.sensors.rst new file mode 100644 index 0000000..9cf6702 --- /dev/null +++ b/docs/api/robot.sensors.rst @@ -0,0 +1,37 @@ +robot.sensors package +===================== + +Submodules +---------- + +robot.sensors.color module +-------------------------- + +.. automodule:: robot.sensors.color + :members: + :show-inheritance: + :undoc-members: + +robot.sensors.distance module +----------------------------- + +.. automodule:: robot.sensors.distance + :members: + :show-inheritance: + :undoc-members: + +robot.sensors.proximity module +------------------------------ + +.. automodule:: robot.sensors.proximity + :members: + :show-inheritance: + :undoc-members: + +Module contents +--------------- + +.. automodule:: robot.sensors + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..3c63122 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import os +import sys +from datetime import datetime + +# Add project src to path so autodoc can import modules +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +SRC = os.path.join(ROOT, "src") +if SRC not in sys.path: + sys.path.insert(0, SRC) + + +project = "Robot Library (Python)" +author = "PeraSwarm" +copyright = f"{datetime.now().year}, {author}" + +extensions = [ + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", +] + +autosummary_generate = True +autodoc_default_options = { + "members": True, + "undoc-members": True, + "show-inheritance": True, +} + +napoleon_google_docstring = True +napoleon_numpy_docstring = False + +# If you cannot or do not want to install heavy deps, you can mock them here +# autodoc_mock_imports = ["paho", "paho.mqtt", "paho.mqtt.client"] + +templates_path = ["_templates"] +html_static_path = ["_static"] + +# html_theme = "furo" +html_theme = "sphinx_rtd_theme" +html_theme_options = { + "collapse_navigation": False, + "navigation_depth": 3, + "titles_only": False, + "prev_next_buttons_location": "bottom", + "sticky_navigation": False, + "style_nav_header_background": "#3468af", +} + + +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} + +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".venv", ".github"] diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..957b2d8 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,18 @@ +--- +title: Robot Library Python +--- + +# Robot Library Documentation + +Welcome to the documentation for the Python robot library. This site includes installation instructions, usage guides, and an auto-generated API reference. + +## Contents + +```{toctree} +:maxdepth: 2 + +README +installation +usage +api/index +``` diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..629df15 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,45 @@ +--- +title: Installation +--- + +# Installation + +Requirements: + +- Python 3.8+ +- `paho-mqtt` (installed automatically when installing the package) + +## Robot Library Installation + +```bash +git clone git@github.com:Pera-Swarm/robot-library-python.git +cd robot-library-python +python -m venv .venv +. .venv/bin/activate # Windows: .venv\\Scripts\\activate +pip install -U pip +pip install -e . +``` + +## Building Docs locally + +From the repository root: + +```bash +pip install -r docs/requirements.txt +sphinx-apidoc -o docs/api src/robot +python -m sphinx -b html docs docs/_build/html +``` + +Or from inside the `docs/` directory: + +```bash +pip install -r requirements.txt +make apidoc +make html +``` + +Use the below command to serve the docs with live reload: + +```bash +make livehtml +``` diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..55caf1b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,9 @@ +furo>=2024.8.6 +Sphinx>=7.2 +myst-parser>=2.0 +sphinx-autodoc-typehints>=2.2 +sphinx-autobuild==2024.8.25 +sphinx_rtd_theme==7.3.1 + +# Ensure imports succeed during autodoc +paho-mqtt>=2.0 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..2fcb39c --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,115 @@ +--- +title: Usage +--- + +# Usage Guide + +This guide shows how to configure MQTT, build a `VirtualRobot`, use motion control, read sensors, send communications, and control indicators. + +## Configure MQTT + +```python +from robot import MQTTSettings + +MQTTSettings.server = "localhost" +MQTTSettings.port = 1883 +MQTTSettings.user_name = "" +MQTTSettings.password = "" +MQTTSettings.channel = "v1" +``` + +## Subclass `VirtualRobot` + +```python +from robot import VirtualRobot +from robot.interfaces import RobotState + +class MyRobot(VirtualRobot): + def loop(self): + if self.state == RobotState.RUN: + # read sensors, move, and communicate here + self.delay(1000) + +robot = MyRobot(1, 0, 0, 90) +robot.start() +robot.run() +``` + +## Motion Control + +Use `MotionController` directly or through a `Robot` via `self.motion`. + +```python +from robot import MotionController + +motion = MotionController() # standalone with a stub coordinate +motion.move(150, 150, duration=1000) # forward 1 second +motion.rotate_degree(120, 90) # rotate 90 degrees +``` + +In a robot loop: + +```python +self.motion.move(180, 180, duration=500) +self.motion.rotate_degree(160, -45) +``` + +Configure geometry and speeds via `robot.configs.robot_settings.RobotSettings`. + +## Sensors + +Sensors are initialized in `Robot.setup()` and available on the robot instance. + +Distance: + +```python +dist_cm = self.dist_sensor.get_distance() +``` + +Color: + +```python +rgb = self.color_sensor.get_color() # RGBColorType +print(rgb.get_r(), rgb.get_g(), rgb.get_b()) +``` + +Proximity (multi-angle): + +```python +# Optionally configure angles first +self.proximity_sensor.set_angles([0, 45, 90, 135, 180]) +reading = self.proximity_sensor.get_proximity() +print(reading.distances()) +for c in reading.colors(): + print(c.to_string_color()) +``` + +## Communications + +Simple broadcast-like messages: + +```python +self.simple_comm.send_message("hello swarm") +self.simple_comm.send_message_with_distance("tag", distance=10) +``` + +Directed messages (to controller routing topic): + +```python +self.directed_comm.send_message("to leader") +``` + +Override `communication_interrupt(self, msg: str)` in your robot to handle inbound messages. + +## Indicators (NeoPixel) + +```python +self.neo_pixel.change_color(0, 128, 255) +``` + +## Extensibility Guidelines + +- Subclass `VirtualRobot` to add behaviors; implement `loop()` and optionally `sensor_interrupt`/`communication_interrupt`. +- New sensors/indicators: follow `robot.sensors.abstract_sensor` / `robot.indicators.abstract_indicator`. Subscribe to topics in `__init__`, implement `handle_subscription`, and publish using `RobotMqttClient`. +- MQTT is namespaced by `MQTTSettings.channel` (e.g., `v1/robot/create`), mirroring the original Java design. +