diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..2f14041 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,160 @@ +# Tank Controller Python - Copilot Instructions + +## Project Overview + +**Purpose**: Python port of TankController for Raspberry Pi, controlling an alkalinity titrator for ocean acidification research. + +**Scale**: ~8,790 lines Python, 102 files, 32 tests. State machine architecture controlling pH probes, temperature sensors, syringe pumps, heaters, LCD display. + +**Tech Stack**: Python 3.9-3.11, pipenv, pytest, tkinter. Raspberry Pi with Adafruit CircuitPython hardware libraries, includes mocks for development. + +## Build and Test Instructions + +### Essential Prerequisites & Dependencies +**ALWAYS install pipenv first**: `pip install pipenv` + +**CRITICAL**: Always run `pipenv sync -d` before any other command (includes dev dependencies for testing/linting). + +**Known Issue**: First run can take 5-10 minutes; may timeout on slow networks. Retry if needed. + +### Running Tests +```bash +./test.sh # Runs: pipenv sync -d && pipenv run pytest -vv +pipenv run pytest -vv # Direct (after pipenv sync -d) +``` + +**Auto-Configuration**: `conftest.py` sets `mock_config.MOCK_ENABLED = True` automatically. Tests run in ~1-2 seconds. + +### Running the Application + +**GUI Mode** (for development/testing without hardware): +```bash +./run_gui.sh # Runs: pipenv sync -d && pipenv run python main.py -gui +``` + +**Hardware Mode** (requires Raspberry Pi with connected devices): +```bash +./run.sh # Runs: pipenv sync && pipenv run python main.py +``` + +**Device Installation** (Raspberry Pi only): +```bash +./device_install.sh # Installs system packages: apt-get update, pipenv, rpi.gpio +``` + +### Linting and Code Quality + +**Tools**: flake8, pylint, isort, codespell (all configured in repo root) + +**Quick Reference**: +- `flake8 ` - Max line 120, ignores E203/F401/W503 +- `pylint ` - Python 3.11, max line 120 (ignore config warnings) +- `isort --check-only ` - Black-compatible import ordering +- `codespell ` - Spell checking + +**CI**: GitHub Actions runs Super-Linter (ghcr.io/github/super-linter:slim-v4) on all PRs. + +## Repository Structure + +### Root Files +- `main.py` - Entry point; accepts `-gui` flag to enable mock mode +- `conftest.py` - Pytest configuration; sets MOCK_ENABLED=True for all tests +- `Pipfile` / `Pipfile.lock` - Dependency definitions (managed by pipenv) +- `.flake8`, `.pylintrc`, `.isort.cfg`, `.codespellrc` - Linter configurations +- `*.sh` - Shell scripts for common operations (test, run, run_gui, device_install) + +### Source Code Layout + +**titration/** - Main application code +- `titrator.py` - Main Titrator class (state machine controller) +- `titrator_driver.py` - Threading setup for GUI and titrator loop +- `gui.py` - Tkinter-based GUI for mock mode +- `mock_config.py` - Global flag: `MOCK_ENABLED` (controls hardware vs. mock) +- `devices/` - Hardware abstraction layer + - `library.py` - Main device imports (Keypad, LiquidCrystal, PHProbe, etc.) + - Hardware implementations: `keypad.py`, `liquid_crystal.py`, `ph_probe.py`, `temperature_probe.py`, `syringe_pump.py`, `stir_control.py`, `temperature_control.py` + - Mock implementations: `*_mock.py` files (used when MOCK_ENABLED=True) +- `ui_state/` - State machine UI states + - `ui_state.py` - Base class for all states + - `main_menu.py` - Initial state + - `titration/` - Titration states (initial, manual, automatic, setup, calibrate_ph) + - `calibration/` - Calibration states (ph, temp, setup) + - `update_settings/` - Settings modification states + - `user_value/` - User input states (solution_weight, salinity, etc.) + - `prime_pump/` - Pump priming states + - `demo_mode/` - Hardware demo states + +**test/** - Mirror structure of titration/ with `*_test.py` files +- 32 test files organized by module +- Uses pytest and pytest-mock + +**arduino/** - Arduino firmware for stepper motor control +- `Pi-ArduinoStepStickDriver/Pi-ArduinoStepStickDriver.ino` - Controls syringe pump stepper motor via serial communication from Pi + +### GitHub Actions Workflows + +1. **pytest.yml** - Python 3.9/3.10/3.11 matrix, runs on all pushes/PRs (~2-3 min) +2. **linter.yml** - Super-Linter on non-main pushes and PRs (~3-5 min) +3. **check-spelling.yml** - Codespell on PRs to main/master + +## Key Architecture Patterns + +**State Machine**: All states inherit from `UIState` with methods: `start()`, `handle_key(key)`, `loop()`, `name()` + +**Hardware Abstraction**: `titration/devices/` classes auto-switch to mocks when `mock_config.MOCK_ENABLED = True` + +**Threading**: Titrator loop in daemon thread, GUI on main thread (see `titrator_driver.py`) + +## Common Pitfalls and Solutions + +| Problem | Solution | +|---------|----------| +| `ModuleNotFoundError` for adafruit/gpiozero/digitalio | Hardware-specific; ensure `mock_config.MOCK_ENABLED = True` (tests auto-set via `conftest.py`) | +| `pipenv sync` timeout | Network issue; retry (can take 5-10 min) | +| Test import errors | Verify `conftest.py` loaded and sets `MOCK_ENABLED = True` | +| GUI won't start | Install tkinter: `apt-get install python3-tk` | +| CI linting failures | Run locally: `flake8`, `pylint`, `isort --check-only`, `codespell` | + +## Making Changes + +**Before**: 1) `pipenv sync -d` 2) `./test.sh` 3) Check similar code style + +**After**: 1) Lint (`flake8`, `pylint`, `isort --check-only`) 2) `./test.sh` 3) Add tests if needed 4) `isort` to fix imports + +**Style**: 120 char lines, snake_case (functions/vars), PascalCase (classes), docstrings on classes/functions, 4 spaces, import order: stdlib→3rd-party→local + +## Validation Steps + +**Full validation sequence** (run before submitting PR): +```bash +# 1. Install dependencies +pipenv sync -d + +# 2. Run all tests +./test.sh + +# 3. Run linters on modified files +flake8 +pylint +isort --check-only +codespell + +# 4. Test the application (optional but recommended) +./run_gui.sh # Verify GUI still works +``` + +## Additional Notes + +- **Never modify Pipfile.lock directly** - use `pipenv install ` or `pipenv install --dev ` +- **Device hardware** is Raspberry Pi specific - all development should use mock mode unless on actual hardware +- **Arduino code** is separate - Python communicates with Arduino via serial for stepper motor control +- **Logging**: The application prints extensive debug output to console (see `titrator.py` loop() and update_state()) +- **Data files**: Generated data files (CSV, JSON) should go in `data/` directory (gitignored except structure) + +## Trust These Instructions + +These instructions have been validated by running commands and inspecting the actual repository state. If you encounter discrepancies: +1. First verify you're running commands from the repository root +2. Check that `pipenv` is installed and working (`pipenv --version`) +3. Try cleaning state: `rm -rf ~/.local/share/virtualenvs/TankController*` and start over +4. Only if instructions are provably incorrect should you search for alternative approaches