Files

465 lines
14 KiB
Markdown

# Development Guide
**Last Updated: May 18, 2026**
This guide provides instructions for setting up a development environment, running tests, and contributing to the Akita Meshtastic Meshcore Bridge (AMMB) project.
## Setting Up Development Environment
Use Python 3.10 or newer for development and test runs. The dev toolchain now
requires `pytest` 9.0.3+ to address CVE-2025-71176, and upstream only ships
that fix for Python 3.10+.
1. **Clone the repository:**
```bash
git clone https://github.com/AkitaEngineering/akita-meshtastic-meshcore-bridge.git
cd akita-meshtastic-meshcore-bridge
```
2. **Create and activate a Python virtual environment:**
```bash
python -m venv venv
# On Windows: .\venv\Scripts\activate
# On Linux/macOS: source venv/bin/activate
```
Using a virtual environment isolates project dependencies.
3. **Install runtime and development dependencies:**
```bash
pip install -r requirements.txt
pip install -r requirements-dev.txt
```
This installs runtime dependencies (`meshtastic`, `meshcore`, `asyncio-mqtt`, `textual`, `pyserial`, `pypubsub`, `paho-mqtt`) and development tools (`pytest`, `pytest-cov`, `flake8`, `mypy`).
## Project Structure
```
akita-meshtastic-meshcore-bridge/
├── ammb/ # Main package
│ ├── __init__.py
│ ├── api.py # Sync REST API server
│ ├── api_async.py # Async REST API server
│ ├── bridge.py # Sync bridge orchestrator
│ ├── bridge_async.py # Async bridge orchestrator
│ ├── config_handler.py # Configuration management
│ ├── health.py # Health monitoring
│ ├── meshcore_handler.py # Sync serial handler
│ ├── meshcore_async_handler.py # Async serial handler
│ ├── meshtastic_handler.py # Meshtastic network handler
│ ├── message_logger.py # Message persistence
│ ├── metrics.py # Metrics collection
│ ├── mqtt_handler.py # Sync MQTT handler
│ ├── mqtt_async_handler.py # Async MQTT handler
│ ├── protocol.py # Serial protocol handlers
│ ├── rate_limiter.py # Rate limiting
│ ├── tui.py # Textual command center
│ ├── utils.py # Utility functions
│ └── validator.py # Message validation
├── tests/ # Test suite
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_config_handler.py
│ ├── test_protocol.py
│ ├── test_tui.py
│ └── test_run_bridge_tui.py
├── docs/ # Documentation
│ ├── api.md
│ ├── architecture.md
│ ├── configuration.md
│ ├── development.md
│ └── usage.md
├── examples/ # Example files
│ ├── config.ini.example
│ └── meshcore_simulator.py
├── run_bridge.py # Sync runtime entry point
├── run_bridge_async.py # Async runtime entry point
├── run_bridge_tui.py # Full-screen terminal command center entry point
├── requirements.txt # Runtime dependencies
├── requirements-dev.txt # Development dependencies
└── README.md # Project overview
```
## Running Tests
The project uses `pytest` for automated testing.
1. **Ensure your virtual environment is active.**
2. **Navigate to the project root directory.**
3. **Run all tests:**
```bash
pytest
```
This will discover and run all tests located in the `tests/` directory.
4. **Run tests with coverage report:**
```bash
pytest --cov=ammb --cov-report term-missing
```
This runs the tests and generates a report showing which lines of the source code in the `ammb/` directory were executed by the tests.
5. **Run specific test file:**
```bash
pytest tests/test_protocol.py
```
6. **Run focused command-center tests:**
```bash
pytest tests/test_tui.py tests/test_run_bridge_tui.py
```
7. **Run with verbose output:**
```bash
pytest -v
```
8. **Smoke-test runtime entrypoints when changing startup behavior or operator UX:**
```bash
python run_bridge.py
python run_bridge_tui.py
python run_bridge_async.py
```
If the command center exits with an unhandled error, inspect `ammb_tui_crash.log` in the project root.
## Code Style and Linting
We use `flake8` for checking code style against PEP 8 guidelines and common errors.
1. **Ensure your virtual environment is active.**
2. **Navigate to the project root directory.**
3. **Run flake8:**
```bash
flake8 ammb/ tests/ run_bridge.py run_bridge_async.py run_bridge_tui.py
```
This will report any style violations or potential errors. Aim for zero reported issues.
4. **Check specific file:**
```bash
flake8 ammb/bridge.py
```
### Project linting policy ✅
- **Max line length:** 79 characters (flake8 E501). When long lines are found, prefer **targeted wrapping or splitting** (for example: split long strings, break complex expressions, or use short helper variables) rather than increasing the line length limit.
- **Third-party packages:** Do **not** edit files under `.venv` or other external package directories to satisfy linter rules. Instead, exclude those directories from lint runs (we include `.venv` in the project's `.flake8` file).
- **Logging:** Prefer parameterized logging calls (e.g., `logger.info("Connected to %s", port)`) instead of long f-strings to keep messages shorter and avoid unnecessary formatting overhead.
- **Fix process:** When addressing E501 issues in project files, make conservative, behavior-preserving edits (wrap strings, reflow docstrings, or adjust logging). Re-run tests and `mypy` after each change to ensure no regressions.
Example: In this revision we fixed several E501 cases in `ammb/` and `examples/` by wrapping long strings and using parameterized logging; tests and `mypy` were re-run to confirm the project remains correct.
## Static Type Checking
We use `mypy` for static type checking to catch potential type-related errors before runtime.
1. **Ensure your virtual environment is active.**
2. **Navigate to the project root directory.**
3. **Run mypy:**
```bash
mypy ammb/ run_bridge.py run_bridge_async.py run_bridge_tui.py
```
This will analyze the type hints in the code and report any inconsistencies or errors. Aim for zero reported issues.
4. **Check specific module:**
```bash
mypy ammb/bridge.py
```
## Code Quality Standards
### Type Hints
All function signatures should include type hints:
```python
def process_message(message: Dict[str, Any]) -> bool:
"""Process a message and return success status."""
...
```
### Documentation Strings
All public functions and classes should have docstrings:
```python
class MessageHandler:
"""Handles message processing and validation."""
def validate(self, data: str) -> bool:
"""
Validate message data.
Args:
data: Message data to validate
Returns:
True if valid, False otherwise
"""
...
```
### Error Handling
Use appropriate exception handling:
```python
try:
result = risky_operation()
except SpecificException as e:
logger.error(f"Operation failed: {e}", exc_info=True)
return None
```
### Thread Safety
All shared data structures must use locks:
```python
class ThreadSafeCounter:
def __init__(self):
self._count = 0
self._lock = threading.Lock()
def increment(self):
with self._lock:
self._count += 1
```
## Contribution Guidelines
We welcome contributions! Please follow these steps:
1. **Fork the repository** on GitHub.
2. **Clone your fork** locally:
```bash
git clone https://github.com/YOUR_FORK_USERNAME/akita-meshtastic-meshcore-bridge.git
```
3. **Create a new branch** for your feature or bug fix:
```bash
git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-description
```
4. **Set up your development environment** as described above.
5. **Make your changes.** Ensure you:
* Follow the existing code style
* Add type hints to all functions
* Write docstrings for public APIs
* Add tests for new features or bug fixes
* Update documentation if necessary
* Ensure all tests pass (`pytest`)
* Ensure linters pass (`flake8 ammb/ tests/ run_bridge.py run_bridge_async.py run_bridge_tui.py`)
* Ensure type checks pass (`mypy ammb/ run_bridge.py run_bridge_async.py run_bridge_tui.py`)
6. **Commit your changes** with clear and descriptive commit messages:
```bash
git commit -m "Add feature: description of what was added"
git commit -m "Fix bug: description of what was fixed"
```
7. **Push your branch** to your fork:
```bash
git push origin feature/your-feature-name
```
8. **Open a Pull Request (PR)** from your fork's branch to the `main` branch of the original repository.
9. **Clearly describe** the changes made in the PR description and link to any relevant issues.
10. **Respond to feedback** or requested changes during the code review process.
## Adding New Features
### Adding New Serial Protocols
To support a different serial protocol:
1. Create a new class in `ammb/protocol.py` that inherits from `MeshcoreProtocolHandler`.
2. Implement the required methods:
```python
class YourProtocol(MeshcoreProtocolHandler):
def read(self, serial_port) -> Optional[bytes]:
"""Read data from serial port."""
...
def encode(self, data: Dict[str, Any]) -> Optional[bytes]:
"""Encode dictionary to bytes."""
...
def decode(self, raw_data: bytes) -> Optional[Dict[str, Any]]:
"""Decode bytes to dictionary."""
...
```
3. Update the `get_serial_protocol_handler()` factory function in `ammb/protocol.py`:
```python
_serial_protocol_handlers = {
'json_newline': JsonNewlineProtocol,
'raw_serial': RawSerialProtocol,
'your_protocol': YourProtocol, # Add here
}
```
4. Add the new protocol name as an option for the `SERIAL_PROTOCOL` setting in:
* `configuration.md`
* `examples/config.ini.example`
5. Add tests for your new protocol handler in `tests/test_protocol.py`.
### Adding New External Transports
To add a new external transport (e.g., HTTP, WebSocket):
1. Create a new handler class similar to `MeshcoreHandler` or `MQTTHandler`:
```python
class YourTransportHandler:
def __init__(self, config, to_meshtastic_queue, from_meshtastic_queue, shutdown_event):
# Initialize with metrics, health, validator, rate_limiter
...
def connect(self) -> bool:
# Implement connection logic
...
def start_publisher(self):
# Start background threads if needed
...
def stop(self):
# Clean shutdown
...
```
2. Integrate with existing systems:
* Use `get_metrics()` for metrics collection
* Use `get_health_monitor()` for health tracking
* Use `MessageValidator` for validation
* Use `RateLimiter` for rate limiting
3. Add to `Bridge` class initialization in `ammb/bridge.py`.
4. Update configuration handler to support new transport settings.
5. Add tests for the new transport handler.
### Adding New API Endpoints
To add new REST API endpoints:
1. Add handler method in `BridgeAPIHandler` class in `ammb/api.py`:
```python
def _handle_your_endpoint(self):
"""Handle your new endpoint."""
data = {"your": "data"}
self._send_response(200, data)
```
2. Add route in `do_GET()` or `do_POST()` method:
```python
elif path == '/api/your_endpoint':
self._handle_your_endpoint()
```
3. Update API documentation in `usage.md`.
## Testing Guidelines
### Unit Tests
Write unit tests for individual functions and classes:
```python
def test_message_validation():
validator = MessageValidator()
valid_msg = {"destination": "^all", "text": "Hello"}
is_valid, error = validator.validate_meshtastic_message(valid_msg)
assert is_valid
assert error is None
```
### Integration Tests
Write integration tests for component interactions:
```python
def test_bridge_initialization():
config = load_config("test_config.ini")
bridge = Bridge(config)
assert bridge.meshtastic_handler is not None
assert bridge.external_handler is not None
```
### Test Fixtures
Use pytest fixtures for common test setup:
```python
@pytest.fixture
def test_config(tmp_path):
config_path = tmp_path / "config.ini"
# Create test configuration
return config_path
```
## Debugging
### Enable Debug Logging
Set `LOG_LEVEL = DEBUG` in `config.ini` for detailed logging.
### Use Python Debugger
Add breakpoints in code:
```python
import pdb; pdb.set_trace()
```
Or use IDE debugger with breakpoints.
### Check Metrics
Use the REST API to check metrics:
```bash
curl http://localhost:8080/api/metrics
```
### Check Health Status
Use the REST API to check health:
```bash
curl http://localhost:8080/api/health
```
## Release Process
When preparing a release:
1. Update version in `ammb/__init__.py`
2. Update `CHANGELOG.md` with release notes
3. Update documentation dates
4. Run full test suite
5. Run linters and type checkers
6. Smoke-test the relevant operator entrypoints (`run_bridge.py`, `run_bridge_tui.py`, and/or `run_bridge_async.py`)
7. Create git tag
8. Push to repository
## Getting Help
If you need help with development:
1. Check existing documentation
2. Review code comments
3. Check test examples
4. Open an issue on GitHub
5. Contact maintainers