diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b6e5000 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + pip install flake8 + flake8 src tests + - name: Run tests + run: | + pip install pytest + PYTHONPATH=src pytest diff --git a/.github/codeql.yml b/.github/workflows/codeql.yml similarity index 100% rename from .github/codeql.yml rename to .github/workflows/codeql.yml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..58b2ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Example Author + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 4329ff7..bbae27b 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -# robot-library-python \ No newline at end of file +# Robot Library Python + +A modular Python library for building robot applications. It provides components +for motion control, sensor access, communication, and indicators. + +## Installation + +```bash +pip install -e . +``` + +## Usage + +```python +from robot.motion import MotionController + +controller = MotionController() +``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d0f2425 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Documentation + +Project documentation goes here. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..fb458c9 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,3 @@ +# Examples + +Example scripts demonstrating robot usage will be placed here. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..eedc316 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "robot-library-python" +version = "0.1.0" +description = "A modular robot control library" +authors = [{name = "Example Author", email = "author@example.com"}] +readme = "README.md" +requires-python = ">=3.8" +dependencies = [] + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/src/robot/__init__.py b/src/robot/__init__.py new file mode 100644 index 0000000..d27dffa --- /dev/null +++ b/src/robot/__init__.py @@ -0,0 +1,11 @@ +"""Top-level package for robot library.""" + +from .robot_base import Robot +from .mqtt_client import RobotMqttClient +from .motion import MotionController + +__all__ = [ + "Robot", + "RobotMqttClient", + "MotionController", +] diff --git a/src/robot/communication/__init__.py b/src/robot/communication/__init__.py new file mode 100644 index 0000000..f96ca88 --- /dev/null +++ b/src/robot/communication/__init__.py @@ -0,0 +1,9 @@ +"""Communication subpackage.""" + +from .simple_comm import SimpleCommunication +from .directed_comm import DirectedCommunication + +__all__ = [ + "SimpleCommunication", + "DirectedCommunication", +] diff --git a/src/robot/communication/directed_comm.py b/src/robot/communication/directed_comm.py new file mode 100644 index 0000000..c0317d8 --- /dev/null +++ b/src/robot/communication/directed_comm.py @@ -0,0 +1,7 @@ +"""Directed communication module.""" + + +class DirectedCommunication: + """Placeholder directed communication class.""" + + pass diff --git a/src/robot/communication/simple_comm.py b/src/robot/communication/simple_comm.py new file mode 100644 index 0000000..7ae7394 --- /dev/null +++ b/src/robot/communication/simple_comm.py @@ -0,0 +1,7 @@ +"""Simple communication module.""" + + +class SimpleCommunication: + """Placeholder simple communication class.""" + + pass diff --git a/src/robot/helpers/__init__.py b/src/robot/helpers/__init__.py new file mode 100644 index 0000000..76c58a1 --- /dev/null +++ b/src/robot/helpers/__init__.py @@ -0,0 +1,9 @@ +"""Helper utilities for robot library.""" + +from .coordinate import Coordinate +from .robot_mqtt import RobotMQTT + +__all__ = [ + "Coordinate", + "RobotMQTT", +] diff --git a/src/robot/helpers/coordinate.py b/src/robot/helpers/coordinate.py new file mode 100644 index 0000000..09e5a61 --- /dev/null +++ b/src/robot/helpers/coordinate.py @@ -0,0 +1,7 @@ +"""Coordinate tracking helper.""" + + +class Coordinate: + """Placeholder coordinate tracker.""" + + pass diff --git a/src/robot/helpers/robot_mqtt.py b/src/robot/helpers/robot_mqtt.py new file mode 100644 index 0000000..0af763c --- /dev/null +++ b/src/robot/helpers/robot_mqtt.py @@ -0,0 +1,7 @@ +"""MQTT helper functions.""" + + +class RobotMQTT: + """Placeholder MQTT helper.""" + + pass diff --git a/src/robot/indicators/__init__.py b/src/robot/indicators/__init__.py new file mode 100644 index 0000000..3617af0 --- /dev/null +++ b/src/robot/indicators/__init__.py @@ -0,0 +1,5 @@ +"""Indicator subpackage.""" + +from .neopixel import NeoPixel + +__all__ = ["NeoPixel"] diff --git a/src/robot/indicators/neopixel.py b/src/robot/indicators/neopixel.py new file mode 100644 index 0000000..0cd1144 --- /dev/null +++ b/src/robot/indicators/neopixel.py @@ -0,0 +1,7 @@ +"""NeoPixel indicator controller.""" + + +class NeoPixel: + """Placeholder NeoPixel controller.""" + + pass diff --git a/src/robot/motion.py b/src/robot/motion.py new file mode 100644 index 0000000..3945773 --- /dev/null +++ b/src/robot/motion.py @@ -0,0 +1,7 @@ +"""Motion controller algorithms.""" + + +class MotionController: + """Placeholder motion controller.""" + + pass diff --git a/src/robot/mqtt_client.py b/src/robot/mqtt_client.py new file mode 100644 index 0000000..4367bb4 --- /dev/null +++ b/src/robot/mqtt_client.py @@ -0,0 +1,7 @@ +"""MQTT client wrapper for robot communication.""" + + +class RobotMqttClient: + """Placeholder MQTT client wrapper.""" + + pass diff --git a/src/robot/robot_base.py b/src/robot/robot_base.py new file mode 100644 index 0000000..54d0c4b --- /dev/null +++ b/src/robot/robot_base.py @@ -0,0 +1,7 @@ +"""Base Robot class.""" + + +class Robot: + """Base robot class providing lifecycle management.""" + + pass diff --git a/src/robot/sensors/__init__.py b/src/robot/sensors/__init__.py new file mode 100644 index 0000000..35816b6 --- /dev/null +++ b/src/robot/sensors/__init__.py @@ -0,0 +1,11 @@ +"""Sensor subpackage.""" + +from .distance import DistanceSensor +from .proximity import ProximitySensor +from .color import ColorSensor + +__all__ = [ + "DistanceSensor", + "ProximitySensor", + "ColorSensor", +] diff --git a/src/robot/sensors/color.py b/src/robot/sensors/color.py new file mode 100644 index 0000000..94fe0be --- /dev/null +++ b/src/robot/sensors/color.py @@ -0,0 +1,7 @@ +"""Color sensor implementation.""" + + +class ColorSensor: + """Placeholder color sensor.""" + + pass diff --git a/src/robot/sensors/distance.py b/src/robot/sensors/distance.py new file mode 100644 index 0000000..3c8c035 --- /dev/null +++ b/src/robot/sensors/distance.py @@ -0,0 +1,7 @@ +"""Distance sensor implementation.""" + + +class DistanceSensor: + """Placeholder distance sensor.""" + + pass diff --git a/src/robot/sensors/proximity.py b/src/robot/sensors/proximity.py new file mode 100644 index 0000000..683a83d --- /dev/null +++ b/src/robot/sensors/proximity.py @@ -0,0 +1,7 @@ +"""Proximity sensor implementation.""" + + +class ProximitySensor: + """Placeholder proximity sensor.""" + + pass diff --git a/src/robot_emulator/__init__.py b/src/robot_emulator/__init__.py deleted file mode 100644 index 8f03e21..0000000 --- a/src/robot_emulator/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -#main API -from .base import RobotState \ No newline at end of file diff --git a/src/robot_emulator/base.py b/src/robot_emulator/base.py deleted file mode 100644 index 5b859b8..0000000 --- a/src/robot_emulator/base.py +++ /dev/null @@ -1,98 +0,0 @@ -# Robot emulator base class -# This file holds core framework for robot emulation -# RobotState - define robot state -# Motion - send move/ rotate command over mqtt -# Virtual Robot - base class for robot emulation - -import threading -import time - -import paho.mqtt.client as mqtt - - -class RobotState: - """Define robot states""" - - BEGIN = "BEGIN" - RUN = "RUN" - WAIT = "WAIT" - - -class Motion: - """Handle movement""" - - def __init__(self, mqtt_client, robot_id): - self.client = mqtt_client - self.robot_id = robot_id - - def move_distance(self, speed, distance): - """Move forward a fixed distance""" - self.client.publish( - f"robots/{self.robot_id}/motion", f"MOVE {speed} {distance}" - ) - - def rotate_degree(self, speed, angle): - """Rotate by an angle""" - self.client.publish(f"robots/{self.robot_id}/motion", f"ROTATE {speed} {angle}") - - def stop(self): - """Stop movement""" - self.client.publish(f"robots/{self.robot_id}/motion", "STOP") - - def move(self, left_speed, right_speed, duration): - """Move with differential speeds for a duration""" - self.client.publish( - f"robots/{self.robot_id}/motion", - f"DRIVE {left_speed} {right_speed} {duration}", - ) - - -class VirtualRobot(threading.Thread): - """Base class for virtual robot emulation""" - - # TODO Extend this class and implement 'setup()' and 'loop()' - - def __init__(self, robot_id, x=0, y=0, heading=0, mqtt_config=None): - super().__init__() - self.robot_id = robot_id - self.x = x - self.y = y - self.heading = heading - self.state = RobotState.BEGIN - self.client = mqtt.Client() - self.motion = Motion(self.client, self.robot_id) - - # configure mqtt - if mqtt_config: - self.client.username_pw_set( - mqtt_config.get("username"), mqtt_config.get("password") - ) - self.client.connect( - mqtt_config.get("server", "127.0.0.1"), - mqtt_config.get("port", 1883), - mqtt_config.get("keepalive", 60), - ) - - def setup(self): - """Run once when the robot starts""" - print(f"Robot {self.robot_id} setup complete") - self.state = RobotState.RUN - - def loop(self): - """Main loop, override this method""" - print( - f"Robot {self.robot_id} running at position ({self.x}, {self.y}) heading {self.heading}" - ) - pass - - def run(self): - """Mian execution loop""" - self.setup() - while True: - try: - self.loop() - time.sleep(0.1) # Small delay to prevent CPU overload - - except Exception as e: - print(f"Error in robot {self.robot_id}: {e}") - break diff --git a/src/robot_emulator/maze_following.py b/src/robot_emulator/maze_following.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/robot_emulator/mqtt_client.py b/src/robot_emulator/mqtt_client.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/robot_emulator/obstacle_avoid.py b/src/robot_emulator/obstacle_avoid.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/robot_emulator/sample_robot.py b/src/robot_emulator/sample_robot.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/robot_emulator/sensors.py b/src/robot_emulator/sensors.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..f43e776 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Test package for robot library.""" diff --git a/tests/test_motion.py b/tests/test_motion.py new file mode 100644 index 0000000..e12c3b6 --- /dev/null +++ b/tests/test_motion.py @@ -0,0 +1,9 @@ +"""Tests for the MotionController.""" + +from robot.motion import MotionController + + +def test_motion_controller_instantiation(): + """Instantiate MotionController.""" + controller = MotionController() + assert isinstance(controller, MotionController)